Skip to content

Commit 996e34b

Browse files
cobusgreylingCobus Greyling
andauthored
chore: maintenance — constraints dogfood, welcome workflow, mcp-server npm (#177)
- Add loop-constraints.md and loop-constraints skill (closes audit warning) - Harden welcome-contributors: contents:read + fallback when listFiles fails - Add release-loop-mcp-server.yml and RELEASE.md/README docs for npm publish Co-authored-by: Cobus Greyling <cobusgreyling@Cobuss-MacBook-Pro-2.local>
1 parent a425c42 commit 996e34b

10 files changed

Lines changed: 192 additions & 21 deletions

File tree

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Release loop-mcp-server
2+
3+
on:
4+
push:
5+
tags:
6+
- 'loop-mcp-server-v*'
7+
workflow_dispatch:
8+
inputs:
9+
tag:
10+
description: 'Tag to publish (e.g. loop-mcp-server-v1.0.0)'
11+
required: true
12+
type: string
13+
14+
permissions:
15+
contents: read
16+
id-token: write
17+
18+
jobs:
19+
test-and-publish:
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@v7
23+
with:
24+
ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', github.event.inputs.tag) || github.ref }}
25+
26+
- uses: actions/setup-node@v6
27+
with:
28+
node-version: '22'
29+
registry-url: 'https://registry.npmjs.org'
30+
cache: 'npm'
31+
cache-dependency-path: tools/mcp-server/package-lock.json
32+
33+
- name: Ensure npm supports trusted publishing (OIDC)
34+
run: npm install -g npm@latest
35+
36+
- name: Install, build, test
37+
working-directory: tools/mcp-server
38+
run: |
39+
npm ci
40+
npm run build
41+
npm test
42+
43+
- name: Skip if version already published
44+
id: check
45+
working-directory: tools/mcp-server
46+
run: |
47+
VERSION=$(node -p "require('./package.json').version")
48+
if npm view "@cobusgreyling/loop-mcp-server@${VERSION}" version 2>/dev/null; then
49+
echo "Version ${VERSION} already on npm — skipping publish."
50+
echo "skip=true" >> "$GITHUB_OUTPUT"
51+
else
52+
echo "Publishing @cobusgreyling/loop-mcp-server@${VERSION}"
53+
echo "skip=false" >> "$GITHUB_OUTPUT"
54+
fi
55+
env:
56+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
57+
58+
- name: Publish to npm
59+
if: steps.check.outputs.skip != 'true'
60+
working-directory: tools/mcp-server
61+
run: npm publish --access public --provenance
62+
env:
63+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

‎.github/workflows/welcome-contributors.yml‎

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ on:
1212
- 'docs/**'
1313

1414
permissions:
15+
contents: read
1516
issues: write
1617
pull-requests: write
1718

@@ -24,34 +25,43 @@ jobs:
2425
script: |
2526
const pr = context.payload.pull_request;
2627
const opener = pr.user.login;
27-
const files = await github.paginate(
28-
github.rest.pulls.listFiles,
29-
{ owner: context.repo.owner, repo: context.repo.repo, pull_number: pr.number, per_page: 100 }
28+
const marker = '— loop-engineering maintainers';
29+
30+
const comments = await github.paginate(
31+
github.rest.issues.listComments,
32+
{ owner: context.repo.owner, repo: context.repo.repo, issue_number: pr.number, per_page: 100 }
3033
);
31-
const paths = files.map((f) => f.filename);
34+
if (comments.some((c) => c.body?.includes(marker))) {
35+
core.info('Welcome comment already present; skipping.');
36+
return;
37+
}
38+
39+
let paths = [];
40+
try {
41+
const files = await github.paginate(
42+
github.rest.pulls.listFiles,
43+
{ owner: context.repo.owner, repo: context.repo.repo, pull_number: pr.number, per_page: 100 }
44+
);
45+
paths = files.map((f) => f.filename);
46+
} catch (err) {
47+
core.warning(`Could not list PR files (${err.message}); inferring from title/body.`);
48+
}
49+
3250
const isStory = pr.title.toLowerCase().includes('story') ||
3351
pr.body?.toLowerCase().includes('stories/') ||
3452
paths.some((p) => p.startsWith('stories/'));
3553
const isAdopter = paths.some((p) => p === 'docs/adopters.md');
36-
const isExample = paths.some((p) => p.startsWith('examples/'));
37-
const isDocs = paths.some((p) => p.startsWith('docs/'));
54+
const isExample = paths.some((p) => p.startsWith('examples/')) ||
55+
pr.title.toLowerCase().includes('example');
56+
const isDocs = paths.some((p) => p.startsWith('docs/')) ||
57+
pr.title.toLowerCase().includes('docs:');
3858
3959
let kind = 'a contribution';
4060
if (isStory) kind = 'a production story';
4161
else if (isAdopter) kind = 'an adopter listing';
4262
else if (isExample) kind = 'a tool example';
4363
else if (isDocs) kind = 'a docs improvement';
4464
45-
const marker = '— loop-engineering maintainers';
46-
const comments = await github.paginate(
47-
github.rest.issues.listComments,
48-
{ owner: context.repo.owner, repo: context.repo.repo, issue_number: pr.number, per_page: 100 }
49-
);
50-
if (comments.some((c) => c.body?.includes(marker))) {
51-
core.info('Welcome comment already present; skipping.');
52-
return;
53-
}
54-
5565
const body = [
5666
`Thanks @${opener} for contributing ${kind} — visible, reviewable PRs like this grow the reference for everyone.`,
5767
'',

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ For developers using Grok, Claude Code, Codex, Cursor, and other AI coding agent
9090
| [loop-cost](tools/loop-cost/) | Token spend estimator — `npx @cobusgreyling/loop-cost` |
9191
| [loop-sync](tools/loop-sync/) | Drift detection between `STATE.md` and `LOOP.md` — `npx @cobusgreyling/loop-sync .` |
9292
| [loop-context](tools/loop-context/) | Stateful memory manager + circuit breaker for long runs — `npx @cobusgreyling/loop-context --check --ledger run.json` |
93-
| [loop-mcp-server](tools/mcp-server/) | MCP runtime lookup for patterns, skills, state — `node tools/mcp-server/dist/index.js` (repo v1; npm pending) |
93+
| [loop-mcp-server](tools/mcp-server/) | MCP runtime lookup for patterns, skills, state — `npx @cobusgreyling/loop-mcp-server` |
9494
| [Goal Engineering](https://github.com/cobusgreyling/goal-engineering) | **Companion:** loops discover, goals finish — `/goal` + [stack cookbook](https://github.com/cobusgreyling/goal-engineering/blob/main/docs/stack-cookbook.md) (`npx @cobusgreyling/goal doctor .`) |
9595
| [Stories](stories/) | Real wins and honest failures |
9696
| [Contributor quickstart](https://github.com/cobusgreyling/loop-engineering/discussions/123) | **Help wanted:** 12 scoped `good first issues` — comment *I'll take this* to get assigned |

‎docs/RELEASE.md‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Release playbook — npm packages
22

3-
This repo ships five public npm packages from `tools/`:
3+
This repo ships six public npm packages from `tools/`:
44

55
| Package | Directory | Release tag |
66
|---------|-----------|-------------|
@@ -9,6 +9,7 @@ This repo ships five public npm packages from `tools/`:
99
| `@cobusgreyling/loop-cost` | `tools/loop-cost` | `loop-cost-v*` |
1010
| `@cobusgreyling/loop-sync` | `tools/loop-sync` | `loop-sync-v*` |
1111
| `@cobusgreyling/loop-context` | `tools/loop-context` | `loop-context-v*` |
12+
| `@cobusgreyling/loop-mcp-server` | `tools/mcp-server` | `loop-mcp-server-v*` |
1213

1314
## One-time setup (trusted publishing — recommended)
1415

@@ -21,6 +22,7 @@ Link npm to GitHub, then for **each package** on [npmjs.com](https://www.npmjs.c
2122
| `@cobusgreyling/loop-cost` | `cobusgreyling/loop-engineering` | `release-loop-cost.yml` |
2223
| `@cobusgreyling/loop-sync` | `cobusgreyling/loop-engineering` | `release-loop-sync.yml` |
2324
| `@cobusgreyling/loop-context` | `cobusgreyling/loop-engineering` | `release-loop-context.yml` |
25+
| `@cobusgreyling/loop-mcp-server` | `cobusgreyling/loop-engineering` | `release-loop-mcp-server.yml` |
2426

2527
Names must match **exactly** (case-sensitive). No `NPM_TOKEN` secret is required when trusted publishing is configured.
2628

@@ -58,9 +60,13 @@ git push origin loop-sync-v1.0.0
5860
# loop-context (stateful memory manager + circuit breaker)
5961
git tag loop-context-v1.0.0
6062
git push origin loop-context-v1.0.0
63+
64+
# loop-mcp-server (MCP runtime lookup for patterns, skills, state)
65+
git tag loop-mcp-server-v1.0.0
66+
git push origin loop-mcp-server-v1.0.0
6167
```
6268

63-
Workflows: `.github/workflows/release-loop-audit.yml`, `.github/workflows/release-loop-init.yml`, `.github/workflows/release-loop-cost.yml`, `.github/workflows/release-loop-sync.yml`, `.github/workflows/release-loop-context.yml`.
69+
Workflows: `.github/workflows/release-loop-audit.yml`, `.github/workflows/release-loop-init.yml`, `.github/workflows/release-loop-cost.yml`, `.github/workflows/release-loop-sync.yml`, `.github/workflows/release-loop-context.yml`, `.github/workflows/release-loop-mcp-server.yml`.
6470

6571
## Verify after publish
6672

@@ -70,6 +76,7 @@ npx @cobusgreyling/loop-init --help
7076
npx @cobusgreyling/loop-cost --help
7177
npx @cobusgreyling/loop-sync --help
7278
npx @cobusgreyling/loop-context --help
79+
npx @cobusgreyling/loop-mcp-server --help
7380

7481
mkdir /tmp/loop-init-test && cd /tmp/loop-init-test
7582
npx @cobusgreyling/loop-init . --pattern daily-triage --tool grok --dry-run

‎loop-constraints.md‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Loop Constraints — loop-engineering reference
2+
3+
> The `loop-constraints` skill reads this file at the start of every run.
4+
> Constraints here are **binding** — the agent MUST follow them.
5+
6+
## Push & Merge
7+
- Don't push before telling me
8+
- Never auto-merge to main without human approval
9+
- Always create a draft PR first; let me review before marking ready
10+
11+
## Paths
12+
- Never edit `.env`, `.env.*`, `auth/`, `payments/`, `secrets/`, `credentials/`
13+
- Never edit `tools/loop-audit/src/`, `docs/primitives*.md`, or showcase assets without human review
14+
- Never edit infrastructure configs without human approval
15+
16+
## Code
17+
- Always run tests before proposing a fix (`npm run test:tools` for tool changes)
18+
- Never disable tests to make CI green
19+
- Never refactor unrelated code — one fix per run
20+
- Max 3 fix attempts per item; escalate after
21+
- Enforce the attempt limit mechanically: log each try to `loop-ledger.json` and run `loop-context --check` before retrying (see the `loop-guard` skill)
22+
23+
## Communication
24+
- Always tell me what you're about to do before doing it
25+
- Never close an issue or PR without my approval
26+
27+
## Budget
28+
- If token spend hits 80% of daily cap, switch to report-only
29+
- If loop-pause-all is active, exit immediately
30+
31+
---
32+
<!-- Repo-specific rules above. Add your own below. -->

‎skills/loop-constraints/SKILL.md‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
name: loop-constraints
3+
description: >
4+
Read loop-constraints.md at the start of every run and enforce every rule.
5+
This skill runs BEFORE triage or any action skill. Constraints are binding.
6+
user_invocable: true
7+
---
8+
9+
# Loop Constraints Enforcer
10+
11+
You are the guardrail. Before any other work begins, you MUST:
12+
13+
1. Read `loop-constraints.md` from the project root.
14+
2. Load every rule into your working memory.
15+
3. Check if `loop-pause-all` is active → exit immediately.
16+
4. Apply these rules to EVERY action that follows.
17+
18+
## How to enforce
19+
20+
- Before pushing: re-read the Push & Merge section. If ANY rule blocks it, stop and tell the human.
21+
- Before editing a file: re-read the Paths section. If the path matches a denylist pattern, escalate.
22+
- Before proposing a fix: re-read the Code section. Run tests. One fix per run.
23+
- Before merging: re-read the Push & Merge section. Human must approve.
24+
25+
## Output at start of run
26+
27+
Always begin with a one-line confirmation:
28+
29+
```
30+
Constraints loaded from loop-constraints.md: N rules active.
31+
```
32+
33+
If no `loop-constraints.md` exists, say so and proceed with default safety rules from `docs/safety.md`.
34+
35+
## Interaction with other skills
36+
37+
- `loop-triage` — constraints may override triage priority (e.g. "don't push" means don't act on CI fixes)
38+
- `minimal-fix` — constraints limit what files can be touched
39+
- `loop-verifier` — constraints define denylist paths the verifier must check
40+
- `loop-budget` — constraints may impose stricter budget than loop-budget.md
41+
42+
## Default constraints (when no file exists)
43+
44+
If `loop-constraints.md` is absent, enforce these minimums:
45+
- Never edit `.env`, `.env.*`, `auth/`, `payments/`, `secrets/`, `credentials/`
46+
- Never auto-merge to main
47+
- Never disable tests
48+
- Escalate after 3 failed fix attempts

‎tools/mcp-server/CHANGELOG.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Changelog — @cobusgreyling/loop-mcp-server
2+
3+
## 1.0.0 — 2026-07-06
4+
5+
- Initial npm release
6+
- MCP resources for patterns, skills, state, budget, and audit tools
7+
- `loop_list_patterns`, `loop_estimate_cost`, and related runtime lookup tools

‎tools/mcp-server/README.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ Instead of stuffing all loop documentation into the prompt, agents can query onl
66

77
## Quick Start
88

9-
**v1 ships from this repo** (npm publish pending). From a cloned `loop-engineering` repo:
9+
```bash
10+
npx @cobusgreyling/loop-mcp-server
11+
```
12+
13+
From a cloned `loop-engineering` repo:
1014

1115
```bash
1216
cd tools/mcp-server && npm ci && npm test

‎tools/mcp-server/dist/index.js‎

100644100755
File mode changed.

‎tools/mcp-server/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"README.md"
1212
],
1313
"scripts": {
14-
"build": "tsc",
14+
"build": "tsc && chmod +x dist/index.js",
1515
"test": "npm run build && node --test test/server.test.mjs",
1616
"prepublishOnly": "npm test",
1717
"start": "node dist/index.js"

0 commit comments

Comments
 (0)