nikos 0.2.0 → 0.2.1
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 +2 -1
- package/dist/commands/ci.d.ts +1 -0
- package/dist/commands/ci.js +57 -6
- package/docs/install.md +23 -0
- package/package.json +1 -1
- package/skills/verify/SKILL.md +7 -0
package/README.md
CHANGED
|
@@ -48,6 +48,7 @@ install. More in [docs/install.md](docs/install.md).
|
|
|
48
48
|
|
|
49
49
|
You don't need to learn the commands. Your agent runs them and, after each
|
|
50
50
|
request, says where things stand and offers a short menu of what to do next.
|
|
51
|
+
In Claude Code, Codex and Cursor, it also does this when a session starts.
|
|
51
52
|
Things you can say:
|
|
52
53
|
|
|
53
54
|
- "Set up nikOS here", or "Map what this repo already does"
|
|
@@ -113,7 +114,7 @@ shell can use nikOS as a library: [docs/library.md](docs/library.md). A worked e
|
|
|
113
114
|
| `nikos clear <id>` | Check a story is ready to build |
|
|
114
115
|
| `nikos plan new <id>` | Start the next version of a story's plan |
|
|
115
116
|
| `nikos verify setup` | Add a Cucumber runner for this repo's stack |
|
|
116
|
-
| `nikos ci setup` | Run validate and every story's scenarios on each PR (GitHub Actions) or merge request (GitLab CI) |
|
|
117
|
+
| `nikos ci setup` | Run validate and every story's scenarios on each PR (GitHub Actions) or merge request (GitLab CI), plus a release check you run by hand before shipping |
|
|
117
118
|
| `nikos verify [id]` | Run a story's scenarios, or all of them |
|
|
118
119
|
| `nikos state set <id> <status>` | Move a story; done only if its scenarios pass; cancelled needs a reason |
|
|
119
120
|
| `nikos approve <id>...` | Sign off done stories, in your terminal or (`--via chat`) by answering your agent (writes `intent/approvals.yaml`) |
|
package/dist/commands/ci.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type Stack } from "../verify/detect.js";
|
|
|
2
2
|
export type Host = "github" | "gitlab";
|
|
3
3
|
/** github or gitlab, from the origin remote's url; undefined when it's neither or there's no remote. */
|
|
4
4
|
export declare function detectHost(root: string): Host | undefined;
|
|
5
|
+
export declare function githubReleaseWorkflow(stack: Stack, root: string): string;
|
|
5
6
|
export declare function githubWorkflow(stack: Stack, root: string): string;
|
|
6
7
|
export declare function gitlabJob(stack: Stack, root: string): string;
|
|
7
8
|
export interface CiResult {
|
package/dist/commands/ci.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// `nikos ci setup`: run nikOS's checks on every pull request (GitHub) or merge request (GitLab).
|
|
2
2
|
// The pipeline runs `nikos validate` and every story's scenarios with `nikos verify`, using the
|
|
3
3
|
// nikos version that wrote it, so a red pipeline means the specs or the code broke, not nikos.
|
|
4
|
+
// A second, run-on-demand pipeline checks a release is ready to ship (nikos release check <n>
|
|
5
|
+
// --run): it fails while a story isn't done, its spec changed, or (when approval.policy is
|
|
6
|
+
// required-for-release) a story isn't approved. It's started by hand, with the release number,
|
|
7
|
+
// rather than on every change, because a release in progress is never ready.
|
|
4
8
|
import { spawnSync } from "node:child_process";
|
|
5
9
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
10
|
import { dirname, join } from "node:path";
|
|
@@ -20,12 +24,42 @@ export function detectHost(root) {
|
|
|
20
24
|
return "gitlab";
|
|
21
25
|
return undefined;
|
|
22
26
|
}
|
|
27
|
+
/** The shell steps that install the project and its scenario runner, for a stack. */
|
|
28
|
+
const install = (stack, root) => stack === "python"
|
|
29
|
+
? [existsSync(join(root, "requirements.txt")) ? "pip install -r requirements.txt" : "", "pip install behave"].filter(Boolean)
|
|
30
|
+
: [existsSync(join(root, "package-lock.json")) ? "npm ci" : "npm install"];
|
|
23
31
|
/** The shell steps that install the runner and run the checks, for a stack. */
|
|
24
32
|
function steps(stack, root, nikos) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
return [...install(stack, root), `npx -y ${nikos} validate`, `npx -y ${nikos} verify`];
|
|
34
|
+
}
|
|
35
|
+
/** The release check, run by hand with the release number. RELEASE comes in as an env var, never pasted into the script. */
|
|
36
|
+
const releaseSteps = (stack, root, nikos, variable) => [...install(stack, root), `npx -y ${nikos} release check "$${variable}" --run`];
|
|
37
|
+
export function githubReleaseWorkflow(stack, root) {
|
|
38
|
+
const nikos = `nikos@${version()}`;
|
|
39
|
+
const python = stack === "python" ? ` - uses: actions/setup-python@v5\n with:\n python-version: "3.12"\n` : "";
|
|
40
|
+
return `# Written by nikos ci setup: checks a release is ready to ship. Run it from the Actions tab
|
|
41
|
+
# (Run workflow) with the release number. It fails while a story isn't done, a spec changed
|
|
42
|
+
# after done, or a story needs an approval it doesn't have.
|
|
43
|
+
name: nikos release check
|
|
44
|
+
on:
|
|
45
|
+
workflow_dispatch:
|
|
46
|
+
inputs:
|
|
47
|
+
release:
|
|
48
|
+
description: The release number to check
|
|
49
|
+
required: true
|
|
50
|
+
type: number
|
|
51
|
+
jobs:
|
|
52
|
+
release-check:
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
env:
|
|
55
|
+
RELEASE: \${{ inputs.release }}
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v4
|
|
58
|
+
- uses: actions/setup-node@v4
|
|
59
|
+
with:
|
|
60
|
+
node-version: 22
|
|
61
|
+
${python}${releaseSteps(stack, root, nikos, "RELEASE").map((s) => ` - run: ${s}`).join("\n")}
|
|
62
|
+
`;
|
|
29
63
|
}
|
|
30
64
|
export function githubWorkflow(stack, root) {
|
|
31
65
|
const nikos = `nikos@${version()}`;
|
|
@@ -60,6 +94,15 @@ nikos:
|
|
|
60
94
|
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
|
61
95
|
script:
|
|
62
96
|
${[...setup, ...steps(stack, root, nikos)].map((s) => ` - ${s}`).join("\n")}
|
|
97
|
+
|
|
98
|
+
# Checks a release is ready to ship. Run a pipeline by hand (Build > Pipelines > New pipeline)
|
|
99
|
+
# with the variable NIKOS_RELEASE set to the release number.
|
|
100
|
+
nikos-release:
|
|
101
|
+
image: ${image}
|
|
102
|
+
rules:
|
|
103
|
+
- if: $NIKOS_RELEASE
|
|
104
|
+
script:
|
|
105
|
+
${[...setup, ...releaseSteps(stack, root, nikos, "NIKOS_RELEASE")].map((s) => ` - ${s}`).join("\n")}
|
|
63
106
|
`;
|
|
64
107
|
}
|
|
65
108
|
const GITLAB_JOB = ".gitlab/ci/nikos.yml";
|
|
@@ -84,17 +127,22 @@ export function setupCi(root, opts = {}) {
|
|
|
84
127
|
};
|
|
85
128
|
if (host === "github") {
|
|
86
129
|
write(".github/workflows/nikos.yml", githubWorkflow(stack, root));
|
|
130
|
+
write(".github/workflows/nikos-release.yml", githubReleaseWorkflow(stack, root));
|
|
87
131
|
return { host, created, skipped };
|
|
88
132
|
}
|
|
89
133
|
// GitLab reads one .gitlab-ci.yml, so the job lives in its own file and is included from it.
|
|
90
134
|
write(GITLAB_JOB, gitlabJob(stack, root));
|
|
135
|
+
// A job file from before the release check: say what to add rather than rewrite it.
|
|
136
|
+
const old = skipped.includes(GITLAB_JOB) && !readFileSync(join(root, GITLAB_JOB), "utf8").includes("nikos-release:");
|
|
137
|
+
const releaseTodo = old ? `Add the release check job to ${GITLAB_JOB}:\n${gitlabJob(stack, root).slice(gitlabJob(stack, root).indexOf("# Checks a release"))}` : undefined;
|
|
91
138
|
const main = join(root, ".gitlab-ci.yml");
|
|
92
139
|
if (!existsSync(main)) {
|
|
93
140
|
write(".gitlab-ci.yml", INCLUDE);
|
|
94
|
-
return { host, created, skipped };
|
|
141
|
+
return { host, created, skipped, ...(releaseTodo ? { todo: releaseTodo } : {}) };
|
|
95
142
|
}
|
|
96
143
|
const included = readFileSync(main, "utf8").includes(GITLAB_JOB);
|
|
97
|
-
|
|
144
|
+
const todo = [included ? "" : `Add this to .gitlab-ci.yml:\n${INCLUDE}`, releaseTodo ?? ""].filter(Boolean).join("\n");
|
|
145
|
+
return { host, created, skipped, ...(todo ? { todo } : {}) };
|
|
98
146
|
}
|
|
99
147
|
export function runCi(args) {
|
|
100
148
|
const [sub, ...rest] = args;
|
|
@@ -114,6 +162,9 @@ export function runCi(args) {
|
|
|
114
162
|
if (r.todo)
|
|
115
163
|
console.log(`\n${r.todo}`);
|
|
116
164
|
console.log(`\nEvery ${r.host === "github" ? "pull request" : "merge request"} now runs nikos validate and every story's scenarios.`);
|
|
165
|
+
console.log(r.host === "github"
|
|
166
|
+
? "To check a release is ready to ship, run the \"nikos release check\" workflow from the Actions tab with its number."
|
|
167
|
+
: "To check a release is ready to ship, run a pipeline by hand with NIKOS_RELEASE set to its number.");
|
|
117
168
|
return 0;
|
|
118
169
|
}
|
|
119
170
|
catch (err) {
|
package/docs/install.md
CHANGED
|
@@ -32,6 +32,10 @@ add the marketplace first, then install:
|
|
|
32
32
|
From a terminal, the same commands are `claude plugin marketplace add
|
|
33
33
|
nikovijay/NikOS` and `claude plugin install nikos@nikos`.
|
|
34
34
|
|
|
35
|
+
At the start of each session in a repo with a story map, the plugin's hook
|
|
36
|
+
(`hooks/hooks.json`) tells the agent where things stand and what to suggest
|
|
37
|
+
next.
|
|
38
|
+
|
|
35
39
|
## Codex
|
|
36
40
|
|
|
37
41
|
```
|
|
@@ -41,6 +45,14 @@ codex plugin marketplace add nikovijay/NikOS
|
|
|
41
45
|
Then install nikOS from the Plugins directory. Update it later with `codex
|
|
42
46
|
plugin marketplace upgrade`.
|
|
43
47
|
|
|
48
|
+
The plugin ships the same session-start hook as Claude Code
|
|
49
|
+
(`hooks/hooks.json`). Codex runs a plugin's hooks only after you review and
|
|
50
|
+
trust them, and asks again when they change. Until you trust it, nikOS works
|
|
51
|
+
without it: the agent runs `nikos suggest` itself after each request. Codex's
|
|
52
|
+
docs say plugin hooks run for plugins installed manually in the Codex desktop
|
|
53
|
+
app, and that plugins with hooks can't be listed in its public plugin
|
|
54
|
+
directory.
|
|
55
|
+
|
|
44
56
|
## Cursor
|
|
45
57
|
|
|
46
58
|
Cursor installs plugins from a marketplace in its settings rather than a
|
|
@@ -50,6 +62,11 @@ then Add Marketplace, then Import from Repo), and install nikOS from it.
|
|
|
50
62
|
To try it without a marketplace, copy the repo to
|
|
51
63
|
`~/.cursor/plugins/local/nikos` and run "Developer: Reload Window".
|
|
52
64
|
|
|
65
|
+
The plugin's `sessionStart` hook (`hooks/cursor-hooks.json`) adds where things
|
|
66
|
+
stand and what to suggest next to each new conversation, in a repo with a story
|
|
67
|
+
map. Cursor runs it without waiting, so on a slow first run (while `npx` fetches
|
|
68
|
+
nikos) it can land after your first message. It doesn't run in cloud agents.
|
|
69
|
+
|
|
53
70
|
## Packs
|
|
54
71
|
|
|
55
72
|
Packs are extra tools that install on their own, next to nikOS or without it.
|
|
@@ -126,6 +143,12 @@ name fields again on 2026-11-05 (no page showed a version):
|
|
|
126
143
|
- Codex: https://developers.openai.com/plugins/build/plugins. The plugin
|
|
127
144
|
title is `interface.displayName` in `.codex-plugin/plugin.json`. The
|
|
128
145
|
marketplace title is the same field on `.agents/plugins/marketplace.json`.
|
|
146
|
+
- Hooks, read on 2026-11-06: Cursor's https://cursor.com/docs/agent/hooks
|
|
147
|
+
(`sessionStart`, JSON output with `additional_context`, `timeout` in seconds;
|
|
148
|
+
a plugin points at its hooks with `hooks` in `.cursor-plugin/plugin.json`),
|
|
149
|
+
and Codex's plugin page above and https://learn.chatgpt.com/docs/hooks
|
|
150
|
+
(`SessionStart`, the same format as Claude Code, plain stdout added as
|
|
151
|
+
context, hooks run only once trusted).
|
|
129
152
|
|
|
130
153
|
All three find skills as `skills/<name>/SKILL.md` at the plugin root.
|
|
131
154
|
`test/install.test.ts` checks the manifests agree with each other and with
|
package/package.json
CHANGED
package/skills/verify/SKILL.md
CHANGED
|
@@ -56,3 +56,10 @@ GitLab CI (`.gitlab/ci/nikos.yml`, included from `.gitlab-ci.yml`). Pass
|
|
|
56
56
|
`--github` or `--gitlab` when there's no remote yet. If the repo already has a
|
|
57
57
|
`.gitlab-ci.yml`, it prints the `include:` lines to add: add them, and show the
|
|
58
58
|
person the change.
|
|
59
|
+
|
|
60
|
+
It also adds a release check the person runs by hand when a release is meant to
|
|
61
|
+
ship: the "nikos release check" workflow on GitHub (Actions tab, Run workflow,
|
|
62
|
+
with the release number), or on GitLab a pipeline run with `NIKOS_RELEASE` set to
|
|
63
|
+
it. It fails while a story isn't done, a spec changed after done, or (when
|
|
64
|
+
`approval.policy` is `required-for-release`) a story isn't approved. Tell the
|
|
65
|
+
person how to run it; don't run it on every change.
|