@se-studio/skills 1.9.7 → 1.9.9
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @se-studio/skills
|
|
2
2
|
|
|
3
|
+
## 1.9.9
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 96e1908: Add fleet-status so an agent session lists every local checkout, its drift, and its worktrees before and after the work.
|
|
8
|
+
|
|
9
|
+
## 1.9.8
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Document role-gated publish, and allow browser CORS on the OAuth token and register routes.
|
|
14
|
+
|
|
3
15
|
## 1.9.7
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Read-only status of every registry checkout on this machine.
|
|
4
|
+
* Run from any directory: node packages/skills/references/agent-session/fleet-status.mjs
|
|
5
|
+
*/
|
|
6
|
+
import { execFileSync } from 'node:child_process';
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
import { dirname, join } from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
|
|
12
|
+
const registryPath = join(dirname(fileURLToPath(import.meta.url)), 'projects.registry.json');
|
|
13
|
+
const registry = JSON.parse(readFileSync(registryPath, 'utf8'));
|
|
14
|
+
|
|
15
|
+
function expand(path) {
|
|
16
|
+
if (path.startsWith('~/')) return join(homedir(), path.slice(2));
|
|
17
|
+
return path;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function git(cwd, args) {
|
|
21
|
+
try {
|
|
22
|
+
return execFileSync('git', args, {
|
|
23
|
+
cwd,
|
|
24
|
+
encoding: 'utf8',
|
|
25
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
26
|
+
}).trim();
|
|
27
|
+
} catch (error) {
|
|
28
|
+
const stderr = error.stderr?.toString().trim() || error.message;
|
|
29
|
+
return { error: stderr.split('\n')[0] };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function ignored(file) {
|
|
34
|
+
const base = file.split('/').pop();
|
|
35
|
+
if (base === 'skills-lock.json' || base === 'next-env.d.ts' || base === 'redirects.ts') {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return file === '.agents/skills' || file.startsWith('.agents/skills/');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function statusPaths(cwd) {
|
|
42
|
+
const raw = git(cwd, ['status', '--porcelain']);
|
|
43
|
+
if (raw.error) return raw;
|
|
44
|
+
const tracked = [];
|
|
45
|
+
const untracked = [];
|
|
46
|
+
for (const line of raw.split('\n').filter(Boolean)) {
|
|
47
|
+
const path = line.slice(3).split(' -> ').pop();
|
|
48
|
+
if (ignored(path)) continue;
|
|
49
|
+
if (line.startsWith('??')) untracked.push(path);
|
|
50
|
+
else tracked.push(`${line.slice(0, 2).trim()} ${path}`);
|
|
51
|
+
}
|
|
52
|
+
return { tracked, untracked };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function worktrees(cwd, primary) {
|
|
56
|
+
const raw = git(cwd, ['worktree', 'list', '--porcelain']);
|
|
57
|
+
if (raw.error) return [];
|
|
58
|
+
const trees = [];
|
|
59
|
+
let current = null;
|
|
60
|
+
for (const line of raw.split('\n')) {
|
|
61
|
+
if (line.startsWith('worktree ')) {
|
|
62
|
+
if (current) trees.push(current);
|
|
63
|
+
current = { path: line.slice(9), branch: '(detached)' };
|
|
64
|
+
} else if (current && line.startsWith('branch ')) {
|
|
65
|
+
current.branch = line.slice('branch refs/heads/'.length);
|
|
66
|
+
} else if (current && line.startsWith('HEAD ')) {
|
|
67
|
+
current.head = line.slice(5, 12);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (current) trees.push(current);
|
|
71
|
+
return trees.filter((tree) => tree.path !== primary);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function printList(label, items, limit = 12) {
|
|
75
|
+
const shown = items.slice(0, limit);
|
|
76
|
+
for (const item of shown) console.log(` ${label} ${item}`);
|
|
77
|
+
if (items.length > limit) console.log(` ${label} +${items.length - limit} more`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log(`fleet-status ${new Date().toISOString().slice(0, 10)}`);
|
|
81
|
+
|
|
82
|
+
for (const project of registry.projects) {
|
|
83
|
+
const path = expand(project.path);
|
|
84
|
+
const branch = project.branch;
|
|
85
|
+
console.log('');
|
|
86
|
+
const inside = git(path, ['rev-parse', '--is-inside-work-tree']);
|
|
87
|
+
if (inside.error || inside !== 'true') {
|
|
88
|
+
console.log(`${project.key} MISSING ${path}`);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const fetched = git(path, ['fetch', '--prune', 'origin', branch]);
|
|
93
|
+
const current = git(path, ['branch', '--show-current']);
|
|
94
|
+
const head = git(path, ['rev-parse', '--short', 'HEAD']);
|
|
95
|
+
const ahead = git(path, ['rev-list', '--count', `origin/${branch}..HEAD`]);
|
|
96
|
+
const behind = git(path, ['rev-list', '--count', `HEAD..origin/${branch}`]);
|
|
97
|
+
const wrong = current !== branch ? ` primary is ${current}` : '';
|
|
98
|
+
const drift =
|
|
99
|
+
ahead.error || behind.error ? ' origin unknown' : ` ahead ${ahead} behind ${behind}`;
|
|
100
|
+
console.log(`${project.key} ${branch} ${head.error ? '?' : head}${drift}${wrong}`);
|
|
101
|
+
if (fetched.error) console.log(` fetch: ${fetched.error}`);
|
|
102
|
+
|
|
103
|
+
const status = statusPaths(path);
|
|
104
|
+
if (status.error) {
|
|
105
|
+
console.log(` status: ${status.error}`);
|
|
106
|
+
} else {
|
|
107
|
+
printList('dirty', status.tracked);
|
|
108
|
+
printList('untracked', status.untracked);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
for (const tree of worktrees(path, path)) {
|
|
112
|
+
const when = git(tree.path, ['log', '-1', '--format=%cs']);
|
|
113
|
+
const date = when.error ? '?' : when;
|
|
114
|
+
const feature = tree.branch.startsWith('feature/') || tree.branch.startsWith('cursor/');
|
|
115
|
+
if (!feature) {
|
|
116
|
+
console.log(` checkout ${tree.path} ${tree.branch} ${tree.head ?? ''} ${date}`);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const extra = git(tree.path, ['rev-list', '--count', `origin/${branch}..HEAD`]);
|
|
120
|
+
const aheadOf = extra.error ? '?' : extra;
|
|
121
|
+
console.log(
|
|
122
|
+
` worktree ${tree.path} ${tree.branch} ${tree.head ?? ''} ${date} commits not in origin/${branch}: ${aheadOf}`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -118,13 +118,15 @@
|
|
|
118
118
|
{
|
|
119
119
|
"key": "eagle",
|
|
120
120
|
"displayName": "Eagle Website",
|
|
121
|
-
"path": "~/source/customers/
|
|
121
|
+
"path": "~/source/customers/eaglecap/eagle-website",
|
|
122
122
|
"worktreesRoot": "~/source/customers/eagle/worktrees",
|
|
123
|
-
"branch": "
|
|
123
|
+
"branch": "develop",
|
|
124
124
|
"productionBranch": "main",
|
|
125
125
|
"type": "customer",
|
|
126
|
+
"contentfulSpaceId": "dnov2dthcze8",
|
|
127
|
+
"hostedMcp": "cms-edit-eagle",
|
|
126
128
|
"validate": "pnpm validate",
|
|
127
|
-
"notes": "
|
|
129
|
+
"notes": "Integration is origin/develop. Production is origin/main. cms-edit host builds only on main. Hosted MCP: https://eagle.content.se.studio/api/mcp. This machine's develop checkout is ~/source/customers/eaglecap/eagle-website. Feature worktrees go under worktreesRoot."
|
|
128
130
|
}
|
|
129
131
|
]
|
|
130
132
|
}
|
|
@@ -25,12 +25,7 @@ Use this skill when you need to read or edit content in Contentful using **hoste
|
|
|
25
25
|
|
|
26
26
|
## Safety Rules
|
|
27
27
|
|
|
28
|
-
**
|
|
29
|
-
- Publish any entry
|
|
30
|
-
- Unpublish any entry
|
|
31
|
-
- Archive or delete published entries
|
|
32
|
-
|
|
33
|
-
All `save` operations create **draft** versions. A human must review and publish in Contentful.
|
|
28
|
+
`save` writes **drafts**. Publish, unpublish, and archive use `publish plan`, then `publish apply` after an explicit yes, and only when the signed-in Contentful role allows it. AI Editor cannot. Do not publish as part of save.
|
|
34
29
|
|
|
35
30
|
**Assets:** Upload **is** supported (`asset upload`). Default workflow: **search and reuse** existing assets first (`index sync` → `asset search`). Search by concept (`asset search --concept` / `--concept-all`) finds a subject and a tone, including files no page uses yet. Concepts are not blog tags. When uploading:
|
|
36
31
|
- **Raster/video:** `cms_edit_request_staged_upload` → curl → `cms_edit_media` prepare → wait → catalog → import with real alt. Video, and an image master over 4MB, is a presigned direct PUT (100MB, `intake: direct`) then prepare with that URL. Smaller images still use `stagedUploadIds`. Never `asset upload --staged/--url/--base64` for jpeg/png/webp/gif/mp4. The stored asset is the processed file.
|
|
@@ -204,7 +199,7 @@ Shows a diff of all modified fields across all changed entries.
|
|
|
204
199
|
cms-edit save
|
|
205
200
|
```
|
|
206
201
|
|
|
207
|
-
Writes all changes to Contentful as **drafts**.
|
|
202
|
+
Writes all changes to Contentful as **drafts**. They stay drafts until `publish plan` then `publish apply`, after an explicit yes, and only if the Contentful role allows it.
|
|
208
203
|
|
|
209
204
|
After saving, the session is cleared automatically if all changes were saved.
|
|
210
205
|
|
|
@@ -66,6 +66,16 @@ Integration branches (agents push feature branches here via PR or merge — **ne
|
|
|
66
66
|
|
|
67
67
|
**Never** work in `productionPath`. Agent work uses `path` (develop checkout) only.
|
|
68
68
|
|
|
69
|
+
### Fleet status
|
|
70
|
+
|
|
71
|
+
Run this at the **start** of every coding session, and **again before you say the session is done**:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pnpm --dir ~/source/se/se-core-product fleet-status
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Read the whole report before editing. A tracked dirty file or a worktree is unfinished work: commit it, push it, drop it, or name it in the handoff. Do not start another feature on a dirty primary checkout. The script lists every registry checkout, hides generated paths, and is the only list of those paths.
|
|
78
|
+
|
|
69
79
|
---
|
|
70
80
|
|
|
71
81
|
## Step 2 — Ledger: PRs and worktrees (not yaml)
|
|
@@ -74,8 +84,8 @@ Customer-only work **must not** create or update files in `se-core-product`. Cro
|
|
|
74
84
|
|
|
75
85
|
**What's in flight** (when the user asks, or before a tidy-up):
|
|
76
86
|
|
|
77
|
-
1. **
|
|
78
|
-
2. **
|
|
87
|
+
1. **This machine:** `pnpm --dir ~/source/se/se-core-product fleet-status`.
|
|
88
|
+
2. **Open PRs** targeting `dev` (core) or `develop` (customer) — `gh pr list --base <integration> --state open`. This includes Cursor cloud / other machines. Fleet-status does not see those.
|
|
79
89
|
|
|
80
90
|
Do not invent a second ledger in git. Stale local trees vs merged PRs are a prune problem, not a yaml problem.
|
|
81
91
|
|
|
@@ -351,7 +361,7 @@ Do not merge until the human asks **and** confirms (`production-merge.md`). Not
|
|
|
351
361
|
|
|
352
362
|
| Outcome | Action |
|
|
353
363
|
|---------|--------|
|
|
354
|
-
| Merged / done | Remove the **local** worktree if you created one; the PR close is the record |
|
|
364
|
+
| Merged / done | Remove the **local** worktree if you created one; the PR close is the record. Run fleet-status again and deal with anything it still lists |
|
|
355
365
|
| Parked | Leave the branch + draft PR; say why in chat |
|
|
356
366
|
|
|
357
367
|
```bash
|
|
@@ -372,7 +382,7 @@ Feature: hero-redesign
|
|
|
372
382
|
Branch: feature/om1/hero-redesign
|
|
373
383
|
```
|
|
374
384
|
|
|
375
|
-
Agent must load this skill, run placement checklist, create a **local** worktree (or use the cloud checkout), then proceed. **Do not** write `work/active/*.yaml`.
|
|
385
|
+
Agent must load this skill, run fleet-status, run the placement checklist, create a **local** worktree (or use the cloud checkout), then proceed. **Do not** write `work/active/*.yaml`.
|
|
376
386
|
|
|
377
387
|
---
|
|
378
388
|
|
|
@@ -388,6 +398,6 @@ Agent must load this skill, run placement checklist, create a **local** worktree
|
|
|
388
398
|
| `pedestal` | `develop` | `main` | customer monorepo |
|
|
389
399
|
| `hsd` | `develop` (`develop-hsd-website`) | `production` (`production-hsd-website`) | customer |
|
|
390
400
|
| `highlander` | `develop` | `main` | customer |
|
|
391
|
-
| `eagle` | `
|
|
401
|
+
| `eagle` | `develop` | `main` | customer |
|
|
392
402
|
|
|
393
403
|
Full paths (`path`, `worktreesRoot`, `productionPath`), MCP keys: `projects.registry.json`.
|