@bvdm/delano 0.2.3 → 0.2.5
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/.delano/viewer/README.md +3 -2
- package/.delano/viewer/public/app.js +13 -1
- package/.delano/viewer/public/app.jsx +2312 -0
- package/.delano/viewer/public/delano-mark.svg +4 -0
- package/.delano/viewer/public/index.html +12 -14
- package/.delano/viewer/public/styles.css +1005 -833
- package/.delano/viewer/server.js +46 -5
- package/README.md +63 -3
- package/assets/install-manifest.json +7 -0
- package/assets/payload/.agents/adapters/manifest.schema.json +103 -0
- package/assets/payload/.agents/adapters/spec-kit/adapter.json +71 -0
- package/assets/payload/.agents/hooks/README.md +6 -1
- package/assets/payload/.agents/hooks/codex-session-status.js +123 -0
- package/assets/payload/.agents/schemas/status-transitions.json +35 -0
- package/assets/payload/.agents/scripts/README.md +1 -1
- package/assets/payload/.agents/scripts/check-status-transitions.mjs +171 -2
- package/assets/payload/.agents/scripts/pm/import-spec-kit.sh +605 -0
- package/assets/payload/.agents/scripts/pm/init.sh +31 -2
- package/assets/payload/.agents/scripts/pm/research.sh +296 -0
- package/assets/payload/.agents/scripts/pm/status.sh +135 -28
- package/assets/payload/.agents/scripts/pm/validate.sh +16 -0
- package/assets/payload/.codex/hooks.json +17 -0
- package/assets/payload/.delano/viewer/README.md +3 -2
- package/assets/payload/.delano/viewer/public/app.js +13 -1
- package/assets/payload/.delano/viewer/public/index.html +12 -14
- package/assets/payload/.delano/viewer/public/styles.css +1005 -833
- package/assets/payload/.delano/viewer/server.js +46 -5
- package/assets/payload/.project/templates/decisions.md +18 -0
- package/assets/payload/.project/templates/plan.md +17 -0
- package/assets/payload/.project/templates/spec.md +12 -0
- package/assets/payload/.project/templates/task.md +6 -0
- package/assets/payload/.project/templates/workstream.md +1 -0
- package/package.json +4 -2
- package/src/cli/commands/install.js +2 -1
- package/src/cli/commands/state.js +689 -0
- package/src/cli/commands/viewer.js +2 -1
- package/src/cli/commands/wrapper.js +29 -5
- package/src/cli/index.js +120 -7
- package/src/cli/lib/install.js +179 -2
- package/src/cli/lib/project-state.js +918 -0
package/.delano/viewer/server.js
CHANGED
|
@@ -12,7 +12,16 @@ const { spawn, spawnSync } = require('node:child_process');
|
|
|
12
12
|
const repoRoot = path.resolve(process.env.DELANO_VIEWER_ROOT || path.resolve(__dirname, '..', '..'));
|
|
13
13
|
const projectRoot = path.join(repoRoot, '.project');
|
|
14
14
|
const publicRoot = path.join(__dirname, 'public');
|
|
15
|
-
const
|
|
15
|
+
const DEFAULT_PORT = 3977;
|
|
16
|
+
const MAX_PORT = 65535;
|
|
17
|
+
const MAX_PORT_ATTEMPTS = 100;
|
|
18
|
+
const startPort = normalizePort(process.env.DELANO_VIEWER_PORT || process.env.PORT, DEFAULT_PORT);
|
|
19
|
+
|
|
20
|
+
function normalizePort(value, fallback) {
|
|
21
|
+
const parsed = Number(value || fallback);
|
|
22
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > MAX_PORT) return fallback;
|
|
23
|
+
return parsed;
|
|
24
|
+
}
|
|
16
25
|
|
|
17
26
|
function isInside(parent, child) {
|
|
18
27
|
const rel = path.relative(parent, child);
|
|
@@ -332,6 +341,7 @@ function sendStatic(res, pathname) {
|
|
|
332
341
|
const ext = path.extname(resolved).toLowerCase();
|
|
333
342
|
const mimeMap = {
|
|
334
343
|
'.js': 'text/javascript',
|
|
344
|
+
'.jsx': 'text/javascript',
|
|
335
345
|
'.css': 'text/css',
|
|
336
346
|
'.svg': 'image/svg+xml',
|
|
337
347
|
'.png': 'image/png',
|
|
@@ -340,7 +350,7 @@ function sendStatic(res, pathname) {
|
|
|
340
350
|
'.webp': 'image/webp',
|
|
341
351
|
'.ico': 'image/x-icon',
|
|
342
352
|
};
|
|
343
|
-
const isText = ext === '.js' || ext === '.css' || ext === '.svg' || ext === '' || ext === '.html';
|
|
353
|
+
const isText = ext === '.js' || ext === '.jsx' || ext === '.css' || ext === '.svg' || ext === '' || ext === '.html';
|
|
344
354
|
const type = mimeMap[ext] || 'text/html';
|
|
345
355
|
const headers = isText ? { 'content-type': `${type}; charset=utf-8` } : { 'content-type': type };
|
|
346
356
|
res.writeHead(200, headers);
|
|
@@ -384,6 +394,37 @@ const server = http.createServer((req, res) => {
|
|
|
384
394
|
}
|
|
385
395
|
});
|
|
386
396
|
|
|
387
|
-
server
|
|
388
|
-
|
|
389
|
-
|
|
397
|
+
function listenWithPortFallback(server, firstPort, host = '127.0.0.1') {
|
|
398
|
+
let port = firstPort;
|
|
399
|
+
let attempts = 0;
|
|
400
|
+
|
|
401
|
+
const listen = () => {
|
|
402
|
+
server.once('error', onError);
|
|
403
|
+
server.listen(port, host);
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
const onError = (error) => {
|
|
407
|
+
if (error.code === 'EADDRINUSE' && port < MAX_PORT && attempts < MAX_PORT_ATTEMPTS) {
|
|
408
|
+
attempts += 1;
|
|
409
|
+
port += 1;
|
|
410
|
+
listen();
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
console.error(`Failed to start Delano viewer on ${host}:${port}: ${error.message}`);
|
|
415
|
+
process.exitCode = 1;
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
const onListening = () => {
|
|
419
|
+
server.removeListener('error', onError);
|
|
420
|
+
const address = server.address();
|
|
421
|
+
const actualPort = typeof address === 'object' && address ? address.port : port;
|
|
422
|
+
const skipped = actualPort !== firstPort ? ` (${firstPort} was unavailable)` : '';
|
|
423
|
+
console.log(`Delano read-only viewer: http://${host}:${actualPort}${skipped}`);
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
server.on('listening', onListening);
|
|
427
|
+
listen();
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
listenWithPortFallback(server, startPort);
|
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ The npm package is intentionally thin. It distributes the approved runtime paylo
|
|
|
15
15
|
## Delano CLI
|
|
16
16
|
|
|
17
17
|
- Package: `@bvdm/delano`
|
|
18
|
-
- Current package version: `0.2.
|
|
18
|
+
- Current package version: `0.2.4`
|
|
19
19
|
- Binary: `delano`
|
|
20
20
|
- Commands: `onboarding`, `install`, `viewer`, `init`, `validate`, `status`, `next`
|
|
21
21
|
- Primary goal: bootstrap a repo safely, expose local delivery state clearly, and keep runtime gates verifiable
|
|
@@ -72,6 +72,8 @@ delano install --yes
|
|
|
72
72
|
delano viewer
|
|
73
73
|
delano validate
|
|
74
74
|
delano init <slug> "<Project Name>" [owner] [lead]
|
|
75
|
+
delano import-spec-kit <slug> <source-md> [--name <project-name>] [--owner <owner>] [--lead <lead>] [--json]
|
|
76
|
+
delano research <project-slug> <research-slug> [--title <title>] [--question <question>] [--json]
|
|
75
77
|
```
|
|
76
78
|
|
|
77
79
|
Command intent:
|
|
@@ -80,6 +82,8 @@ Command intent:
|
|
|
80
82
|
- `delano viewer` launches the read-only local UI for `.project` contracts
|
|
81
83
|
- `delano validate` checks whether the runtime and required assets are in place
|
|
82
84
|
- `delano init` creates a delivery project inside a repository that already has Delano installed
|
|
85
|
+
- `delano import-spec-kit` creates a planned Delano project from the first supported Spec Kit-style markdown fixture and then runs validation
|
|
86
|
+
- `delano research` creates repo-native research intake files inside an existing Delano project and then runs validation
|
|
83
87
|
|
|
84
88
|
`delano init` usage:
|
|
85
89
|
|
|
@@ -94,8 +98,36 @@ Notes:
|
|
|
94
98
|
- `lead` defaults to `owner`
|
|
95
99
|
- this is the right command for an agent to scaffold a new delivery project after `delano install`
|
|
96
100
|
|
|
101
|
+
`delano import-spec-kit` usage:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
delano import-spec-kit <slug> <source-md> [--name <project-name>] [--owner <owner>] [--lead <lead>] [--json]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Notes:
|
|
108
|
+
|
|
109
|
+
- the source markdown must use the initial supported shape documented in `docs/spec-kit/import-contract.md`
|
|
110
|
+
- agents should prefer named options over positional metadata, and `--json` when parsing the result
|
|
111
|
+
- imported artifacts start in planned/ready states and still have to pass Delano validation, probe, and evidence gates
|
|
112
|
+
- the command is additive and refuses to overwrite an existing `.project/projects/<slug>/` folder
|
|
113
|
+
|
|
114
|
+
`delano research` usage:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
delano research <project-slug> <research-slug> [--title <title>] [--question <question>] [--owner <owner>] [--json]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Notes:
|
|
121
|
+
|
|
122
|
+
- use this before mutating `spec.md`, `plan.md`, workstreams, or tasks when intent is unclear
|
|
123
|
+
- research files live under `.project/projects/<project-slug>/research/<research-slug>/`
|
|
124
|
+
- agents should use `--json` when parsing the result
|
|
125
|
+
- research findings must fold forward into canonical Delano artifacts or be explicitly closed as no-action
|
|
126
|
+
|
|
97
127
|
## How to use Delano after install
|
|
98
128
|
|
|
129
|
+
For a fast guided path, start with [Delano in the First 15 Minutes](docs/first-15-minutes.md).
|
|
130
|
+
|
|
99
131
|
Recommended first step:
|
|
100
132
|
|
|
101
133
|
```bash
|
|
@@ -111,6 +143,7 @@ npx -y @bvdm/delano@latest onboarding --approve-agents-analysis
|
|
|
111
143
|
npx -y @bvdm/delano@latest viewer
|
|
112
144
|
npx -y @bvdm/delano@latest validate
|
|
113
145
|
npx -y @bvdm/delano@latest status
|
|
146
|
+
npx -y @bvdm/delano@latest status --open --brief
|
|
114
147
|
npx -y @bvdm/delano@latest next -- --all
|
|
115
148
|
```
|
|
116
149
|
|
|
@@ -121,8 +154,11 @@ delano onboarding
|
|
|
121
154
|
delano viewer
|
|
122
155
|
delano validate
|
|
123
156
|
delano status
|
|
157
|
+
delano status --open --brief
|
|
124
158
|
delano next -- --all
|
|
125
159
|
delano init <slug> "<Project Name>" [owner] [lead]
|
|
160
|
+
delano import-spec-kit <slug> <source-md> --json
|
|
161
|
+
delano research <project-slug> <research-slug> --title "Research title" --question "Primary question" --json
|
|
126
162
|
```
|
|
127
163
|
|
|
128
164
|
The wrapper commands call the existing PM scripts under `.agents/scripts/pm/`. You can also run those scripts directly:
|
|
@@ -130,7 +166,9 @@ The wrapper commands call the existing PM scripts under `.agents/scripts/pm/`. Y
|
|
|
130
166
|
```bash
|
|
131
167
|
bash .agents/scripts/pm/validate.sh
|
|
132
168
|
bash .agents/scripts/pm/status.sh
|
|
169
|
+
bash .agents/scripts/pm/status.sh --open --brief
|
|
133
170
|
bash .agents/scripts/pm/next.sh --all
|
|
171
|
+
bash .agents/scripts/pm/research.sh <project-slug> <research-slug> --title "Research title" --question "Primary question" --json
|
|
134
172
|
```
|
|
135
173
|
|
|
136
174
|
The viewer is packaged with `@bvdm/delano` and serves the selected repository's `.project` files read-only. It defaults to `http://127.0.0.1:3977`; set `DELANO_VIEWER_PORT` or `PORT` to use another port. It indexes `.project/context`, `.project/templates`, and `.project/projects`, then derives artifact roles, statuses, project outlines, task/workstream relationships, snippets, and rendered markdown for local inspection.
|
|
@@ -160,6 +198,28 @@ The CLI does not bundle its own shell or Python runtime.
|
|
|
160
198
|
|
|
161
199
|
The base install payload intentionally excludes top-level adapter entry docs such as `AGENTS.md`, `CLAUDE.md`, `CODEX.md`, `OPENCODE.md`, and `PI.md`. Those remain opt-in only.
|
|
162
200
|
The base install payload includes `.delano/`, including the read-only viewer UI.
|
|
201
|
+
The base install payload also includes `.codex/hooks.json`, a Codex `SessionStart` hook config that injects compact open-project context when Codex hooks are enabled. If a target repo already has `.codex/hooks.json`, `delano install` merges the Delano hook into the existing JSON instead of replacing it. Invalid or non-file hook configs are skipped without blocking the rest of the install.
|
|
202
|
+
|
|
203
|
+
Codex hook activation is intentionally manual:
|
|
204
|
+
|
|
205
|
+
1. Enable hooks for a session with `codex --enable hooks`, or persist the feature in `~/.codex/config.toml`:
|
|
206
|
+
|
|
207
|
+
```toml
|
|
208
|
+
[features]
|
|
209
|
+
hooks = true
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
2. Start Codex in the repository and approve the project trust prompt for the repo-local `.codex/` layer. Codex records trusted projects in `~/.codex/config.toml`, for example:
|
|
213
|
+
|
|
214
|
+
```toml
|
|
215
|
+
[projects."E:\\path\\to\\repo"]
|
|
216
|
+
trust_level = "trusted"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
3. Approve the Delano `SessionStart` hook when Codex asks whether to trust it.
|
|
220
|
+
|
|
221
|
+
Older docs and builds may refer to `[features].codex_hooks`; newer Codex builds warn that this key is deprecated in favor of `[features].hooks`.
|
|
222
|
+
|
|
163
223
|
The installable `.project/context/` pack is seeded from generic templates during packaging; it does not ship Delano's own repo-specific context files into consumer repositories.
|
|
164
224
|
After install, the recommended first step is `delano onboarding`, which requires explicit approval before it reviews `AGENTS.md`.
|
|
165
225
|
|
|
@@ -174,7 +234,7 @@ delano install --no-project-state --force --yes
|
|
|
174
234
|
|
|
175
235
|
The interactive installer presents presets for updating the runtime while preserving project state, updating only skills and project templates, full install or repair, and custom category selection.
|
|
176
236
|
|
|
177
|
-
Install categories are `agent-runtime`, `skills`, `viewer`, `project-context`, `project-templates`, `project-registry`, `project-projects`, `handbook`, and `legacy-installer`. The `--no-project-state` shortcut excludes `.project/context`, `.project/projects`, and `.project/registry`.
|
|
237
|
+
Install categories are `agent-runtime`, `codex-hooks`, `skills`, `viewer`, `project-context`, `project-templates`, `project-registry`, `project-projects`, `handbook`, and `legacy-installer`. The `--no-project-state` shortcut excludes `.project/context`, `.project/projects`, and `.project/registry`.
|
|
178
238
|
|
|
179
239
|
## Optional AGENTS.md / CLAUDE.md snippet
|
|
180
240
|
|
|
@@ -254,7 +314,7 @@ Before the first Actions publish, configure npm trusted publishing for `@bvdm/de
|
|
|
254
314
|
|
|
255
315
|
The package metadata must keep `repository.url` set to `https://github.com/MajesteitBart/delano`; npm validates that value against the GitHub Actions provenance bundle.
|
|
256
316
|
|
|
257
|
-
After trusted publishing is configured, publish by pushing a matching version tag such as `v0.2.
|
|
317
|
+
After trusted publishing is configured, publish by pushing a matching version tag such as `v0.2.4`, or run the `Publish package to npm` workflow manually from `main`. The workflow rebuilds the package payload, checks manifest drift, runs tests, dry-runs the package contents, verifies the version is not already published, and then runs `npm publish --access public` from GitHub Actions using OIDC. A manual `dry_run` input is available to run the same checks without publishing.
|
|
258
318
|
|
|
259
319
|
If npm publish fails after the package checks pass, verify that the npm trusted publisher settings match the repository and workflow filename exactly, and that the workflow has `id-token: write`.
|
|
260
320
|
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
".agents/README.md",
|
|
5
5
|
".agents/adapters/claude/README.md",
|
|
6
6
|
".agents/adapters/codex/README.md",
|
|
7
|
+
".agents/adapters/manifest.schema.json",
|
|
7
8
|
".agents/adapters/opencode/README.md",
|
|
8
9
|
".agents/adapters/pi/README.md",
|
|
10
|
+
".agents/adapters/spec-kit/adapter.json",
|
|
9
11
|
".agents/common/README.md",
|
|
10
12
|
".agents/common/log-safety.js",
|
|
11
13
|
".agents/eval-fixtures/skill-output/invalid/missing-evidence/output.json",
|
|
@@ -14,6 +16,7 @@
|
|
|
14
16
|
".agents/fixtures/linear/issue-snapshot.json",
|
|
15
17
|
".agents/hooks/README.md",
|
|
16
18
|
".agents/hooks/bash-worktree-fix.sh",
|
|
19
|
+
".agents/hooks/codex-session-status.js",
|
|
17
20
|
".agents/hooks/post-tool-logger.js",
|
|
18
21
|
".agents/hooks/session-tracker.js",
|
|
19
22
|
".agents/hooks/user-prompt-logger.js",
|
|
@@ -87,8 +90,10 @@
|
|
|
87
90
|
".agents/scripts/pm/epic-list.sh",
|
|
88
91
|
".agents/scripts/pm/in-progress.sh",
|
|
89
92
|
".agents/scripts/pm/init.sh",
|
|
93
|
+
".agents/scripts/pm/import-spec-kit.sh",
|
|
90
94
|
".agents/scripts/pm/next.sh",
|
|
91
95
|
".agents/scripts/pm/prd-list.sh",
|
|
96
|
+
".agents/scripts/pm/research.sh",
|
|
92
97
|
".agents/scripts/pm/search.sh",
|
|
93
98
|
".agents/scripts/pm/standup.sh",
|
|
94
99
|
".agents/scripts/pm/status.sh",
|
|
@@ -155,6 +160,7 @@
|
|
|
155
160
|
".agents/validation-fixtures/strict/invalid/stale-context/context.md",
|
|
156
161
|
".agents/validation-fixtures/strict/manifest.json",
|
|
157
162
|
".agents/validation-fixtures/strict/valid/minimal-project/task.md",
|
|
163
|
+
".codex/hooks.json",
|
|
158
164
|
".delano/README.md",
|
|
159
165
|
".delano/viewer/README.md",
|
|
160
166
|
".delano/viewer/server.js",
|
|
@@ -168,6 +174,7 @@
|
|
|
168
174
|
".project/registry/linear-map.json",
|
|
169
175
|
".project/registry/migration-map.json",
|
|
170
176
|
".project/templates/completion-summary.md",
|
|
177
|
+
".project/templates/decisions.md",
|
|
171
178
|
".project/templates/plan.md",
|
|
172
179
|
".project/templates/progress-update.md",
|
|
173
180
|
".project/templates/spec.md",
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://delano.dev/schemas/adapter-manifest.schema.json",
|
|
4
|
+
"title": "Delano Adapter Manifest",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"id",
|
|
9
|
+
"name",
|
|
10
|
+
"type",
|
|
11
|
+
"owner",
|
|
12
|
+
"status",
|
|
13
|
+
"summary",
|
|
14
|
+
"commands",
|
|
15
|
+
"generated_files",
|
|
16
|
+
"validation",
|
|
17
|
+
"install",
|
|
18
|
+
"limits"
|
|
19
|
+
],
|
|
20
|
+
"properties": {
|
|
21
|
+
"id": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
|
|
24
|
+
},
|
|
25
|
+
"name": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"minLength": 1
|
|
28
|
+
},
|
|
29
|
+
"type": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"enum": ["agent", "authoring-tool", "sync-tool", "workflow"]
|
|
32
|
+
},
|
|
33
|
+
"owner": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"minLength": 1
|
|
36
|
+
},
|
|
37
|
+
"status": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"enum": ["proposed", "experimental", "stable", "deprecated"]
|
|
40
|
+
},
|
|
41
|
+
"summary": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"minLength": 1
|
|
44
|
+
},
|
|
45
|
+
"commands": {
|
|
46
|
+
"type": "array",
|
|
47
|
+
"minItems": 1,
|
|
48
|
+
"items": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"additionalProperties": false,
|
|
51
|
+
"required": ["name", "description", "input", "output", "writes", "validation"],
|
|
52
|
+
"properties": {
|
|
53
|
+
"name": { "type": "string", "minLength": 1 },
|
|
54
|
+
"description": { "type": "string", "minLength": 1 },
|
|
55
|
+
"input": { "type": "array", "items": { "type": "string" } },
|
|
56
|
+
"output": { "type": "array", "items": { "type": "string" } },
|
|
57
|
+
"writes": { "type": "array", "items": { "type": "string" } },
|
|
58
|
+
"validation": { "type": "array", "items": { "type": "string" } }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"generated_files": {
|
|
63
|
+
"type": "array",
|
|
64
|
+
"items": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"additionalProperties": false,
|
|
67
|
+
"required": ["path", "owner", "mode", "conflict_behavior", "fold_forward"],
|
|
68
|
+
"properties": {
|
|
69
|
+
"path": { "type": "string", "minLength": 1 },
|
|
70
|
+
"owner": { "type": "string", "minLength": 1 },
|
|
71
|
+
"mode": { "type": "string", "enum": ["create-only", "update-owned", "proposal-only", "never-overwrite"] },
|
|
72
|
+
"conflict_behavior": { "type": "string", "enum": ["abort", "diff-required", "operator-approval-required"] },
|
|
73
|
+
"fold_forward": { "type": "string", "minLength": 1 }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"validation": {
|
|
78
|
+
"type": "array",
|
|
79
|
+
"minItems": 1,
|
|
80
|
+
"items": { "type": "string" }
|
|
81
|
+
},
|
|
82
|
+
"install": {
|
|
83
|
+
"type": "object",
|
|
84
|
+
"additionalProperties": false,
|
|
85
|
+
"required": ["categories", "conflict_policy"],
|
|
86
|
+
"properties": {
|
|
87
|
+
"categories": {
|
|
88
|
+
"type": "array",
|
|
89
|
+
"items": { "type": "string" }
|
|
90
|
+
},
|
|
91
|
+
"conflict_policy": {
|
|
92
|
+
"type": "string",
|
|
93
|
+
"minLength": 1
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"limits": {
|
|
98
|
+
"type": "array",
|
|
99
|
+
"minItems": 1,
|
|
100
|
+
"items": { "type": "string" }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "spec-kit",
|
|
3
|
+
"name": "Spec Kit Interop",
|
|
4
|
+
"type": "authoring-tool",
|
|
5
|
+
"owner": "delano-team",
|
|
6
|
+
"status": "experimental",
|
|
7
|
+
"summary": "Imports Spec Kit-style intent artifacts into Delano-governed delivery projects.",
|
|
8
|
+
"commands": [
|
|
9
|
+
{
|
|
10
|
+
"name": "delano import-spec-kit",
|
|
11
|
+
"description": "Create a planned Delano project from a supported Spec Kit-style markdown artifact.",
|
|
12
|
+
"input": ["slug", "source-md", "--name", "--owner", "--lead", "--json"],
|
|
13
|
+
"output": ["human summary", "JSON result with ok, command, project, source, validation"],
|
|
14
|
+
"writes": [".project/projects/<slug>/"],
|
|
15
|
+
"validation": ["delano validate"]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "delano research",
|
|
19
|
+
"description": "Open repo-native research intake for unclear imported intent.",
|
|
20
|
+
"input": ["project-slug", "research-slug", "--title", "--question", "--json"],
|
|
21
|
+
"output": ["human summary", "JSON result with ok, command, project, research, files, validation"],
|
|
22
|
+
"writes": [".project/projects/<project-slug>/research/<research-slug>/"],
|
|
23
|
+
"validation": ["delano validate"]
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"generated_files": [
|
|
27
|
+
{
|
|
28
|
+
"path": ".project/projects/<slug>/spec.md",
|
|
29
|
+
"owner": "spec-kit adapter",
|
|
30
|
+
"mode": "create-only",
|
|
31
|
+
"conflict_behavior": "abort",
|
|
32
|
+
"fold_forward": "canonical spec"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"path": ".project/projects/<slug>/plan.md",
|
|
36
|
+
"owner": "spec-kit adapter",
|
|
37
|
+
"mode": "create-only",
|
|
38
|
+
"conflict_behavior": "abort",
|
|
39
|
+
"fold_forward": "canonical plan"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"path": ".project/projects/<slug>/tasks/*.md",
|
|
43
|
+
"owner": "spec-kit adapter",
|
|
44
|
+
"mode": "create-only",
|
|
45
|
+
"conflict_behavior": "abort",
|
|
46
|
+
"fold_forward": "canonical tasks with evidence gates"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"path": ".project/projects/<project-slug>/research/<research-slug>/",
|
|
50
|
+
"owner": "research intake",
|
|
51
|
+
"mode": "create-only",
|
|
52
|
+
"conflict_behavior": "abort",
|
|
53
|
+
"fold_forward": "spec, plan, decisions, workstreams, tasks, or updates"
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"validation": [
|
|
57
|
+
"delano validate",
|
|
58
|
+
"npm run check:text-safety for Delano repo changes",
|
|
59
|
+
"fixture import smoke before release"
|
|
60
|
+
],
|
|
61
|
+
"install": {
|
|
62
|
+
"categories": ["agent-runtime", "project-templates", "skills"],
|
|
63
|
+
"conflict_policy": "Use existing Delano install allowlist behavior; abort on generated project collisions unless an operator approves a diff-backed change."
|
|
64
|
+
},
|
|
65
|
+
"limits": [
|
|
66
|
+
"Does not replace Spec Kit.",
|
|
67
|
+
"Does not execute imported tasks automatically.",
|
|
68
|
+
"Does not write Linear or GitHub state directly.",
|
|
69
|
+
"Does not depend on Obsidian, OpenClaw, or private local paths."
|
|
70
|
+
]
|
|
71
|
+
}
|
|
@@ -7,5 +7,10 @@ Default hooks:
|
|
|
7
7
|
- `post-tool-logger.js`
|
|
8
8
|
- `user-prompt-logger.js`
|
|
9
9
|
- `bash-worktree-fix.sh`
|
|
10
|
+
- `codex-session-status.js`
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
The logging hooks append JSONL records in `.agents/logs/`.
|
|
13
|
+
|
|
14
|
+
`codex-session-status.js` is used by the optional `.codex/hooks.json` SessionStart
|
|
15
|
+
configuration. It emits `delano status --open --brief` context and fails open if
|
|
16
|
+
the local runtime is not available.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { existsSync } = require("node:fs");
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
const { spawnSync } = require("node:child_process");
|
|
5
|
+
|
|
6
|
+
function findDelanoRoot(startDir) {
|
|
7
|
+
let current = path.resolve(startDir);
|
|
8
|
+
while (true) {
|
|
9
|
+
if (
|
|
10
|
+
existsSync(path.join(current, ".project", "projects")) &&
|
|
11
|
+
existsSync(path.join(current, ".agents", "scripts", "pm", "status.sh"))
|
|
12
|
+
) {
|
|
13
|
+
return current;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const parent = path.dirname(current);
|
|
17
|
+
if (parent === current) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
current = parent;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function toBashPath(filePath) {
|
|
25
|
+
return filePath.replace(/\\/g, "/");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function resolveBash() {
|
|
29
|
+
const candidates = [];
|
|
30
|
+
|
|
31
|
+
if (process.env.DELANO_BASH) {
|
|
32
|
+
candidates.push(process.env.DELANO_BASH);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (process.platform === "win32") {
|
|
36
|
+
candidates.push(
|
|
37
|
+
"C:\\Program Files\\Git\\bin\\bash.exe",
|
|
38
|
+
"C:\\Program Files\\Git\\usr\\bin\\bash.exe"
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const whereResult = spawnSync("where.exe", ["bash"], {
|
|
42
|
+
encoding: "utf8",
|
|
43
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
44
|
+
});
|
|
45
|
+
if (whereResult.status === 0 && whereResult.stdout) {
|
|
46
|
+
candidates.push(...whereResult.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
const whichResult = spawnSync("which", ["bash"], {
|
|
50
|
+
encoding: "utf8",
|
|
51
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
52
|
+
});
|
|
53
|
+
if (whichResult.status === 0 && whichResult.stdout.trim()) {
|
|
54
|
+
candidates.push(whichResult.stdout.trim());
|
|
55
|
+
}
|
|
56
|
+
candidates.push("/usr/bin/bash", "/bin/bash");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return candidates.find((candidate) => candidate && existsSync(candidate)) || null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const root = findDelanoRoot(process.cwd());
|
|
63
|
+
const bashPath = resolveBash();
|
|
64
|
+
if (!root || !bashPath) {
|
|
65
|
+
process.exit(0);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const statusScript = toBashPath(path.join(root, ".agents", "scripts", "pm", "status.sh"));
|
|
69
|
+
const result = spawnSync(bashPath, [statusScript, "--open", "--brief"], {
|
|
70
|
+
cwd: root,
|
|
71
|
+
encoding: "utf8",
|
|
72
|
+
timeout: 4500,
|
|
73
|
+
maxBuffer: 64 * 1024
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if (result.error || result.status !== 0) {
|
|
77
|
+
process.exit(0);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const statusOutput = result.stdout.trim();
|
|
81
|
+
if (!statusOutput) {
|
|
82
|
+
process.exit(0);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const additionalContext = formatStatusContext(statusOutput);
|
|
86
|
+
|
|
87
|
+
console.log(JSON.stringify({
|
|
88
|
+
hookSpecificOutput: {
|
|
89
|
+
hookEventName: "SessionStart",
|
|
90
|
+
additionalContext
|
|
91
|
+
}
|
|
92
|
+
}));
|
|
93
|
+
|
|
94
|
+
function formatStatusContext(rawStatusOutput) {
|
|
95
|
+
const lines = rawStatusOutput.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
96
|
+
const projectLines = lines.filter((line) => (
|
|
97
|
+
!line.startsWith("Delano ") &&
|
|
98
|
+
!/^=+$/.test(line) &&
|
|
99
|
+
!line.startsWith("No open projects")
|
|
100
|
+
));
|
|
101
|
+
|
|
102
|
+
if (projectLines.length === 0) {
|
|
103
|
+
return "Delano startup context. Open projects: none.";
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const projects = projectLines.map(formatProjectLine);
|
|
107
|
+
return `Delano startup context. Open projects: ${projects.join("; ")}.`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function formatProjectLine(line) {
|
|
111
|
+
const match = line.match(/^(\S+)\s+spec=(\S+)\s+plan=(\S+)\s+open_tasks=(\d+)\s+total_tasks=(\d+)$/);
|
|
112
|
+
if (!match) {
|
|
113
|
+
return line;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const [, slug, spec, plan, openTasks, totalTasks] = match;
|
|
117
|
+
return `${slug} (spec=${spec}, plan=${plan}, open_tasks=${openTasks}, total_tasks=${totalTasks})`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = {
|
|
121
|
+
formatProjectLine,
|
|
122
|
+
formatStatusContext
|
|
123
|
+
};
|
|
@@ -21,6 +21,41 @@
|
|
|
21
21
|
"description": "A done task must not close over unresolved local dependencies.",
|
|
22
22
|
"requires": ["all local depends_on task statuses are done"]
|
|
23
23
|
},
|
|
24
|
+
{
|
|
25
|
+
"id": "progressed-task-requires-active-project",
|
|
26
|
+
"status": "in-progress|done",
|
|
27
|
+
"description": "A task must not start or complete while the parent project spec or plan is still planned.",
|
|
28
|
+
"requires": [
|
|
29
|
+
"spec.status is active or complete",
|
|
30
|
+
"plan.status is active or done"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "closed-task-set-requires-closed-project",
|
|
35
|
+
"status": "done|deferred",
|
|
36
|
+
"description": "A project with no open tasks must not remain open through stale spec or plan statuses.",
|
|
37
|
+
"requires": [
|
|
38
|
+
"spec.status is complete or deferred",
|
|
39
|
+
"plan.status is done or deferred"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "progressed-task-requires-active-workstream",
|
|
44
|
+
"status": "in-progress|done",
|
|
45
|
+
"description": "A task must not start or complete before its parent workstream has started.",
|
|
46
|
+
"requires": [
|
|
47
|
+
"in-progress tasks require workstream.status active",
|
|
48
|
+
"done tasks require workstream.status active or done"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": "closed-task-set-requires-closed-workstream",
|
|
53
|
+
"status": "done|deferred",
|
|
54
|
+
"description": "A workstream with no open tasks must not remain open through a stale status.",
|
|
55
|
+
"requires": [
|
|
56
|
+
"workstream.status is done or deferred"
|
|
57
|
+
]
|
|
58
|
+
},
|
|
24
59
|
{
|
|
25
60
|
"id": "blocked-owner-check-back",
|
|
26
61
|
"status": "blocked",
|