@oisincoveney/pipeline 3.23.0 → 3.24.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 CHANGED
@@ -379,6 +379,15 @@ Package and container publishing is owned by GitHub Actions. Do not publish from
379
379
  a workstation with `npm publish`, `semantic-release`, Docker pushes, or direct
380
380
  registry commands.
381
381
 
382
+ The authoritative package version lives in the npm registry, not in this
383
+ repository. `package.json` pins the `0.0.0-development` semantic-release
384
+ sentinel and is never bumped in git; semantic-release derives and publishes the
385
+ real version from commit history at release time. To learn the current version,
386
+ query the registry (`npm view @oisincoveney/pipeline version`) — the repo
387
+ `version` field is intentionally not authoritative. Downstream pins (for example
388
+ pipeline-console) should track the registry via Renovate rather than the git
389
+ field.
390
+
382
391
  Before committing changes in this repository, run:
383
392
 
384
393
  ```shell
@@ -79,10 +79,11 @@ runner_command:
79
79
  # Set up package-owned pipeline support + the opencode model registration
80
80
  # (.opencode/opencode.json, which declares the gpt-5.5-* reasoning selectors)
81
81
  # on every run, so opencode-backed agents in the pod resolve their models
82
- # instead of failing with "Model not found". `moka init` installs the full
83
- # per-machine harness (skills + commands + hooks + rules); --force refreshes
84
- # a pre-baked/version-skewed settings.json instead of refusing it. Idempotent,
85
- # writes no repo-local pipeline config.
82
+ # instead of failing with "Model not found". `moka init` installs Moka host
83
+ # adapters only; the shared harness (skills + hooks + rules) is laid down by
84
+ # chezmoi before this step. --force refreshes a pre-baked/version-skewed
85
+ # settings.json instead of refusing it. Idempotent, writes no repo-local
86
+ # pipeline config.
86
87
  - command: moka
87
88
  args: [init, --force]
88
89
  scheduler:
@@ -10,7 +10,12 @@ function registerBootstrapCommands(program) {
10
10
  console.log(flags.json ? JSON.stringify(result) : formatDoctorResult(result));
11
11
  if (!result.passed) throw new Error("Doctor checks failed.");
12
12
  });
13
- program.command("init").description("Install or refresh moka's slash-command adapters (/moka-execute, /moka-inspect, /moka-quick) plus the singleton MCP gateway host config, globally to ~/.claude, ~/.config/opencode, ~/.codex with no repo-local config. The agent harness (skills, hooks, instruction rules) is provisioned separately from oisin-ee/agent via chezmoi, not by moka.").option("--check", "verify the installed adapters are current; fail if stale").option("--dry-run", "show planned changes without writing files").option("--force", "overwrite manually edited command adapter files").action(async (flags) => {
13
+ program.command("init").description([
14
+ "Install or refresh Moka host adapters (/moka-execute, /moka-inspect, /moka-quick command surfaces,",
15
+ "native-agent projections, and gateway config), globally to ~/.claude, ~/.config/opencode, ~/.codex",
16
+ "with no repo-local config. The shared agent harness (skills, hooks, instruction rules) is provisioned",
17
+ "separately from oisin-ee/agent via chezmoi, not by Moka."
18
+ ].join(" ")).option("--check", "verify the installed adapters are current; fail if stale").option("--dry-run", "show planned changes without writing files").option("--force", "overwrite manually edited command adapter files").action(async (flags) => {
14
19
  const result = await initPipelineProject({
15
20
  ...flags,
16
21
  cwd: process.env.PIPELINE_TARGET_PATH ?? process.cwd()
@@ -61,7 +61,7 @@ function missingFileReferenceWarning(path, value) {
61
61
  }
62
62
  function missingFileReferenceMessage(path, value) {
63
63
  const base = `${path} references missing file '${value}'`;
64
- if (path.startsWith("skills.") && value.startsWith(".agents/skills/")) return `${base}. Run \`moka init\` to install project-local skills with \`npx --yes skills add oisin-ee/agent/skills\`.`;
64
+ if (path.startsWith("skills.") && value.startsWith(".agents/skills/")) return `${base}. Run \`chezmoi apply --refresh-externals always\` to install shared harness skills from oisin-ee/agent.`;
65
65
  return base;
66
66
  }
67
67
  function resolveLintPathReference(projectRoot, ref) {
@@ -1,5 +1,6 @@
1
1
  import { DEFAULT_RUNNER_COMMAND_GIT_COMMITTER } from "../config/schema/catalog.js";
2
2
  import { resolveFactorySeams } from "./exec.js";
3
+ import { githubGitCredentialEnv } from "./git-credentials.js";
3
4
  import { existsSync, mkdtempSync } from "node:fs";
4
5
  import { tmpdir } from "node:os";
5
6
  import { join, resolve } from "node:path";
@@ -80,7 +81,7 @@ async function runCreateExperiment(options) {
80
81
  previews,
81
82
  ...options.templateRef ? { templateRef: options.templateRef } : {},
82
83
  templateSource
83
- }));
84
+ }), { env: githubGitCredentialEnv() });
84
85
  const stampedRegistryEntry = join(stampDir, STAMPED_REGISTRY_DIR, `${name}.yaml`);
85
86
  if (!existsSync(stampedRegistryEntry)) throw new Error(`create-experiment: stamp is missing the registry entry ${STAMPED_REGISTRY_DIR}/${name}.yaml — template contract changed?`);
86
87
  log("create-experiment: committing the stamped tree");
@@ -3,6 +3,7 @@ import { execa } from "execa";
3
3
  //#region src/factory/exec.ts
4
4
  const defaultFactoryExec = (command, args, options) => execa(command, [...args], {
5
5
  ...options?.cwd ? { cwd: options.cwd } : {},
6
+ ...options?.env ? { env: options.env } : {},
6
7
  stdin: "ignore"
7
8
  });
8
9
  const defaultFactoryGit = (cwd, args) => runAuthenticatedGit(cwd, args);
@@ -0,0 +1,37 @@
1
+ import { existsSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join, resolve } from "node:path";
4
+ //#region src/factory/git-credentials.ts
5
+ const DEFAULT_GIT_CREDENTIALS_DIR = "/etc/pipeline/git-credentials";
6
+ /**
7
+ * copier fetches the (private) momokaya-template by spawning its OWN git
8
+ * subprocess (`git ls-remote` / `clone`, via plumbum). `runAuthenticatedGit`
9
+ * only configures credentials per-invocation with `-c` flags on the git calls
10
+ * the lane makes itself, so copier's git sees no github.com credential and
11
+ * dies `fatal: could not read Username for 'https://github.com'`.
12
+ *
13
+ * This materializes a process-wide github.com credential from the runner's
14
+ * mounted git-credentials (username + PAT under /etc/pipeline/git-credentials)
15
+ * as `GIT_CONFIG_*` env any child (and grandchild) git process inherits, so
16
+ * copier's template fetch authenticates. The token is written to a 0600 store
17
+ * file and referenced by a `credential.helper=store` entry — it never lands in
18
+ * a URL or argv. No mounted credentials (local dev) -> empty env, so git falls
19
+ * back to ambient auth.
20
+ */
21
+ function githubGitCredentialEnv(credentialsDir = process.env.PIPELINE_GIT_CREDENTIALS_DIR ?? DEFAULT_GIT_CREDENTIALS_DIR) {
22
+ const usernamePath = resolve(credentialsDir, "username");
23
+ const passwordPath = resolve(credentialsDir, "password");
24
+ if (!(existsSync(usernamePath) && existsSync(passwordPath))) return {};
25
+ const username = readFileSync(usernamePath, "utf8").trim();
26
+ const password = readFileSync(passwordPath, "utf8").trim();
27
+ const storePath = join(mkdtempSync(join(tmpdir(), "factory-git-cred-")), ".git-credentials");
28
+ writeFileSync(storePath, `https://${encodeURIComponent(username)}:${encodeURIComponent(password)}@github.com\n`, { mode: 384 });
29
+ return {
30
+ GIT_CONFIG_COUNT: "1",
31
+ GIT_CONFIG_KEY_0: "credential.helper",
32
+ GIT_CONFIG_VALUE_0: `store --file=${storePath}`,
33
+ GIT_TERMINAL_PROMPT: "0"
34
+ };
35
+ }
36
+ //#endregion
37
+ export { githubGitCredentialEnv };
@@ -1,4 +1,5 @@
1
1
  import { resolveFactorySeams } from "./exec.js";
2
+ import { githubGitCredentialEnv } from "./git-credentials.js";
2
3
  import { committerConfigArgs } from "./create-experiment.js";
3
4
  import { isStampOf, parseCopierAnswers } from "./stamp-answers.js";
4
5
  import { parse } from "yaml";
@@ -101,7 +102,10 @@ async function updateRepo(input) {
101
102
  "--trust",
102
103
  "--defaults",
103
104
  ...input.templateRef ? ["--vcs-ref", input.templateRef] : []
104
- ], { cwd: cloneDir });
105
+ ], {
106
+ cwd: cloneDir,
107
+ env: githubGitCredentialEnv()
108
+ });
105
109
  const status = await git(cloneDir, ["status", "--porcelain"]);
106
110
  if (status.trim().length === 0) return {
107
111
  repo,
@@ -1,13 +1,14 @@
1
1
  import { installCommands } from "./install-commands.js";
2
2
  //#region src/pipeline-init.ts
3
3
  /**
4
- * `moka init` installs only moka's own slash-command adapters
5
- * (`/moka-execute|inspect|quick`) plus the singleton MCP gateway host config,
6
- * globally for Claude Code, Codex, and OpenCode. The shared agent harness
4
+ * `moka init` installs only Moka's own host adapters
5
+ * (`/moka-execute|inspect|quick` command surfaces, native-agent projections,
6
+ * and singleton MCP gateway host config) globally for Claude Code, Codex, and
7
+ * OpenCode. The shared agent harness
7
8
  * (skills, agent hooks, and global instruction rules) is no longer installed by
8
- * moka — it is provisioned from `oisin-ee/agent` via chezmoi (the dotfiles'
9
+ * Moka — it is provisioned from `oisin-ee/agent` via chezmoi (the dotfiles'
9
10
  * `.chezmoiexternal` clone + `run_onchange` harness installer). Keeping moka's
10
- * command adapters here means the runner image (and local dev) still gets the
11
+ * host adapters here means the runner image (and local dev) still gets the
11
12
  * `/moka-*` entrypoints after `chezmoi apply` lays down the harness.
12
13
  */
13
14
  async function initPipelineProject(options = {}) {
@@ -27,17 +28,17 @@ function initInstallerFlags(options) {
27
28
  }
28
29
  const INIT_RESULT_COPY = {
29
30
  install: {
30
- headline: "Initialized moka slash-command adapters:",
31
+ headline: "Initialized Moka host adapters:",
31
32
  fileVerb: "generated",
32
33
  footer: "no repo-local pipeline config files were created"
33
34
  },
34
35
  check: {
35
- headline: "Verified moka slash-command adapters are current:",
36
+ headline: "Verified Moka host adapters are current:",
36
37
  fileVerb: "current",
37
38
  footer: "adapters verified; no changes written"
38
39
  },
39
40
  dryRun: {
40
- headline: "Planned moka slash-command adapters:",
41
+ headline: "Planned Moka host adapters:",
41
42
  fileVerb: "would generate",
42
43
  footer: "dry run; no changes written"
43
44
  }
@@ -51,7 +52,11 @@ function formatPipelineInitResult(result, mode = {}) {
51
52
  const copy = INIT_RESULT_COPY[initResultMode(mode)];
52
53
  return [
53
54
  copy.headline,
54
- "per-machine slash-command adapters (/moka-execute|inspect|quick) installed globally (~/.claude, ~/.config/opencode, ~/.codex); the agent harness (skills, hooks, instruction rules) comes from oisin-ee/agent via chezmoi, not moka",
55
+ [
56
+ "per-machine Moka host adapters (/moka-execute|inspect|quick command surfaces, native-agent projections,",
57
+ "and gateway config) installed globally (~/.claude, ~/.config/opencode, ~/.codex); the shared agent harness",
58
+ "(skills, hooks, instruction rules) comes from oisin-ee/agent via chezmoi, not Moka"
59
+ ].join(" "),
55
60
  ...result.files.map((path) => `${copy.fileVerb} ${path}`),
56
61
  copy.footer
57
62
  ].join("\n");
@@ -127,6 +127,7 @@ function generateSchedulePhaseEffect(context) {
127
127
  config: context.config,
128
128
  entrypointId,
129
129
  phaseContext,
130
+ pullRequestDeliveryRequested: context.payload.delivery.pullRequest,
130
131
  runId: context.payload.run.id,
131
132
  task: context.task,
132
133
  worktreePath: context.worktreePath
@@ -151,8 +151,8 @@ receive explicit grants:
151
151
  - `network`: inherited or disabled.
152
152
  - `output`: text, JSON, JSONL, or JSON Schema output.
153
153
 
154
- Default skills resolve from project-installed skill files created by
155
- `moka init` via `npx --yes skills add oisin-ee/agent/skills`:
154
+ Default skills resolve from shared harness skill files installed from
155
+ `oisin-ee/agent` by chezmoi:
156
156
 
157
157
  ```yaml
158
158
  skills:
@@ -161,11 +161,11 @@ skills:
161
161
  ```
162
162
 
163
163
  Project-authored skill and rule paths resolve from the project root and must
164
- exist for runtime use. If default skill files are missing, run `moka init` to
165
- install them before executing workflows.
164
+ exist for runtime use. If shared harness skill files are missing, run
165
+ `chezmoi apply --refresh-externals always` before executing workflows.
166
166
 
167
- Default agent hooks are copied by `moka init` from private `oisin-ee/agent`.
168
- That source repository has one canonical hook layout:
167
+ Default agent hooks are copied by `bin/install-harness.mjs` from private
168
+ `oisin-ee/agent`. That source repository has one canonical hook layout:
169
169
 
170
170
  ```text
171
171
  hooks/claude-code/
@@ -173,18 +173,10 @@ hooks/codex/
173
173
  hooks/opencode/
174
174
  ```
175
175
 
176
- Moka overlays those folders onto `.claude`, `.codex`, and `.opencode` for
177
- project scope, or the corresponding per-machine host config directories for
178
- global scope. Moka tracks installed hashes in host-local manifests so it can
179
- update unchanged owned hook files, delete owned files removed from the hook
180
- repository, and reject manual drift unless `--force` is used.
181
-
182
- `moka init --skill-scope` (PIPE-83.12) chooses how the default set is installed:
183
- `project` (default) vendors a repo-local copy (`skills add … --copy`,
184
- `skills-lock.json`); `personal` installs once at user/global scope
185
- (`skills add … --global`) so every repo the user opens inherits the skills with
186
- no per-repo copy and no project lockfile — the standardization path for a single
187
- user across many projects.
176
+ The harness installer overlays those folders onto the corresponding per-machine
177
+ host config directories. It tracks installed hashes in host-local manifests so
178
+ it can update unchanged owned hook files, delete owned files removed from the
179
+ hook repository, and reject manual drift unless `--force` is used.
188
180
 
189
181
  MCP-enabled profiles use one gateway grant:
190
182
 
@@ -211,10 +203,10 @@ OpenCode host resources are generated from the same profile registry:
211
203
 
212
204
  - `.opencode/agents/*.md` declares native agents with `mode`, `description`,
213
205
  resolved model, explicit permissions, and task access to generated agents only.
214
- - `.opencode/skills/*/SKILL.md` is installed by `skills add`; Moka only
215
- generates agents, commands, plugins, and project config.
216
- - Additional manually authored OpenCode hook plugins can be copied from
217
- `oisin-ee/agent/hooks/opencode/` by `moka init`.
206
+ - `.agents/skills/*/SKILL.md` is installed by the shared harness; Moka only
207
+ generates agents, commands, plugins, and host config.
208
+ - Additional manually authored OpenCode hook plugins are copied from
209
+ `oisin-ee/agent/hooks/opencode/` by the shared harness installer.
218
210
  - `.opencode/plugins/pipeline-goal-context.ts` projects package-owned
219
211
  continuation context into OpenCode compaction.
220
212
  - `.opencode/opencode.json` contains the gateway MCP config, enables LSP, and
@@ -454,8 +446,9 @@ rather than re-emitting it into every project.
454
446
 
455
447
  ## Troubleshooting
456
448
 
457
- - Missing host resources: run `moka init`; `moka run` loads the installed
449
+ - Missing Moka host adapters: run `moka init`; `moka run` loads the installed
458
450
  package config.
451
+ - Missing shared harness assets: run `chezmoi apply --refresh-externals always`.
459
452
  - Capability error: reduce the profile grants or choose a runner whose declared
460
453
  capabilities include the requested tools, filesystem, network, output, rules,
461
454
  skills, or MCP access.
@@ -47,7 +47,7 @@ host-specific MCP config.
47
47
  3. Configure `mcp_gateway` in package-owned profile config.
48
48
  4. Run `moka mcp gateway reconcile` to render and apply the full ToolHive vMCP
49
49
  backend inventory for the current workspace.
50
- 5. Run `moka init` to write project OpenCode and Claude Code command surfaces and host
50
+ 5. Run `moka init` to write OpenCode and Claude Code command surfaces and host
51
51
  MCP config.
52
52
  6. Run `moka mcp gateway doctor` to verify gateway health and required tools.
53
53
  7. Keep high-risk upstream capabilities controlled by gateway-side policy, not
@@ -32,9 +32,9 @@ moka init --check
32
32
  - OpenCode agents project package profiles as markdown agents with `mode`,
33
33
  `description`, resolved `model`, `permission`, `hidden`, and task permission
34
34
  maps. The primary orchestrator may call only generated package profile agents.
35
- - OpenCode skill files are installed by `npx skills add` during `moka init`;
36
- Moka does not generate `.opencode/skills`. Generated agent
37
- `permission.skill` maps still deny ungranted skills.
35
+ - OpenCode skill files are installed by the shared harness from `oisin-ee/agent`;
36
+ Moka does not generate skill files. Generated agent `permission.skill` maps
37
+ still deny ungranted skills.
38
38
  - `.opencode/opencode.json` includes the singleton `pipeline-gateway` MCP
39
39
  server and enables OpenCode LSP. CLI lint, typecheck, tests, and configured
40
40
  gates remain the blocking verification path; LSP is editor/runtime assistance.
package/package.json CHANGED
@@ -146,7 +146,7 @@
146
146
  "prepack": "nub run build:cli"
147
147
  },
148
148
  "type": "module",
149
- "version": "3.23.0",
149
+ "version": "3.24.0",
150
150
  "description": "Config-driven multi-agent pipeline runner for repository work",
151
151
  "main": "./dist/index.js",
152
152
  "types": "./dist/index.d.ts",