create-egregore 0.19.5 → 0.19.7
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/assets/egregore-launcher.js +411 -25
- package/bin/cli.js +34 -20
- package/lib/local.js +21 -5
- package/lib/pi-runtime.js +186 -0
- package/lib/setup.js +56 -11
- package/lib/ui.js +1 -1
- package/package.json +7 -5
- package/runtime/codex/.codex/spec-manifest.json +1 -1
- package/runtime/codex/AGENTS.md +7 -2
- package/runtime/codex/bin/codex-render-spec.mjs +6 -1
- package/runtime/codex/bin/codex-session-start.sh +5 -59
- package/runtime/codex/bin/codex-skill-render.mjs +7 -5
- package/runtime/codex/bin/lib/context.sh +8 -1
- package/runtime/codex/bin/lib/greeting.sh +12 -108
- package/runtime/codex/bin/lib/metrics.sh +374 -0
- package/runtime/codex/bin/publish-artifact.sh +14 -0
- package/runtime/codex/bin/settings.sh +274 -0
- package/runtime/codex/manifest.json +2 -0
- package/runtime/pi/.codex/config.toml +15 -0
- package/runtime/pi/.codex/hooks/branch-guard.js +242 -0
- package/runtime/pi/.codex/hooks/search-hint.js +65 -0
- package/runtime/pi/.codex/hooks.json +30 -0
- package/runtime/pi/.codex/skills/activity/SKILL.md +70 -0
- package/runtime/pi/.codex/skills/add/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/announce/SKILL.md +54 -0
- package/runtime/pi/.codex/skills/answer/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/archive/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/ask/SKILL.md +59 -0
- package/runtime/pi/.codex/skills/branch/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/checkup/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/commit/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/contribute/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/dark-mode/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/dashboard/SKILL.md +59 -0
- package/runtime/pi/.codex/skills/deep-reflect/SKILL.md +74 -0
- package/runtime/pi/.codex/skills/delete-user/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/egregore-voice/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/emissary/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/env/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/handoff/SKILL.md +67 -0
- package/runtime/pi/.codex/skills/harvest/SKILL.md +107 -0
- package/runtime/pi/.codex/skills/ingest/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/ingest-google/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/ingest-user-interview/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/invite/SKILL.md +40 -0
- package/runtime/pi/.codex/skills/issue/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/me/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/meeting/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/minion/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/note/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/onboarding/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/pr/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/project/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/pull/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/push/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/quest/SKILL.md +77 -0
- package/runtime/pi/.codex/skills/quest-suggest/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/reflect/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/review-pr/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/save/SKILL.md +62 -0
- package/runtime/pi/.codex/skills/scroll/SKILL.md +168 -0
- package/runtime/pi/.codex/skills/scroll/agents/openai.yaml +4 -0
- package/runtime/pi/.codex/skills/scroll/assets/shell.html +542 -0
- package/runtime/pi/.codex/skills/search/SKILL.md +56 -0
- package/runtime/pi/.codex/skills/session-view/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/setup/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/summon/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/sync-repos/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/telegram-connect/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/telemetry/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/test/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/the-spiral/SKILL.md +84 -0
- package/runtime/pi/.codex/skills/thread/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/todo/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/tui-design/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/tutorial/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/update/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/view/SKILL.md +156 -0
- package/runtime/pi/.codex/skills/view/agents/openai.yaml +4 -0
- package/runtime/pi/.codex/skills/view/assets/compose-scaffold.html +221 -0
- package/runtime/pi/.codex/skills/visual-explain/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/wrap/SKILL.md +55 -0
- package/runtime/pi/.codex/spec-manifest.json +114 -0
- package/runtime/pi/.pi/APPEND_SYSTEM.md +255 -0
- package/runtime/pi/.pi/extensions/egregore.ts +324 -0
- package/runtime/pi/.pi/settings.json +6 -0
- package/runtime/pi/.pi/spec-manifest.json +12 -0
- package/runtime/pi/AGENTS.md +350 -0
- package/runtime/pi/bin/activity-data.sh +242 -0
- package/runtime/pi/bin/agent.sh +677 -0
- package/runtime/pi/bin/codex-install-skills.sh +78 -0
- package/runtime/pi/bin/codex-render-spec.mjs +394 -0
- package/runtime/pi/bin/codex-session-start.sh +188 -0
- package/runtime/pi/bin/codex-skill-render.mjs +323 -0
- package/runtime/pi/bin/codex-sync-skills.sh +227 -0
- package/runtime/pi/bin/dashboard-data.sh +178 -0
- package/runtime/pi/bin/graph-batch.sh +60 -0
- package/runtime/pi/bin/graph-op.sh +431 -0
- package/runtime/pi/bin/graph-wal.sh +228 -0
- package/runtime/pi/bin/graph.sh +177 -0
- package/runtime/pi/bin/handoff-pr-backfill.sh +86 -0
- package/runtime/pi/bin/handoff-run.sh +581 -0
- package/runtime/pi/bin/handoff-save-egregore.sh +136 -0
- package/runtime/pi/bin/index-handoff.sh +312 -0
- package/runtime/pi/bin/lib/artifact-id.sh +46 -0
- package/runtime/pi/bin/lib/config.sh +71 -0
- package/runtime/pi/bin/lib/context.sh +542 -0
- package/runtime/pi/bin/lib/dashboard-artifact.sh +142 -0
- package/runtime/pi/bin/lib/ensure-develop.sh +183 -0
- package/runtime/pi/bin/lib/git-sync.sh +288 -0
- package/runtime/pi/bin/lib/greeting.sh +340 -0
- package/runtime/pi/bin/lib/hash.sh +13 -0
- package/runtime/pi/bin/lib/identity.sh +174 -0
- package/runtime/pi/bin/lib/metrics.sh +374 -0
- package/runtime/pi/bin/lib/permissions.sh +106 -0
- package/runtime/pi/bin/lib/time.sh +56 -0
- package/runtime/pi/bin/lib/worktree-links.sh +54 -0
- package/runtime/pi/bin/notify.sh +207 -0
- package/runtime/pi/bin/pi-branch-policy.mjs +15 -0
- package/runtime/pi/bin/pi-product-workflows.mjs +324 -0
- package/runtime/pi/bin/pi-render-spec.mjs +114 -0
- package/runtime/pi/bin/pi-session-start.sh +24 -0
- package/runtime/pi/bin/publish-artifact.sh +201 -0
- package/runtime/pi/bin/publish-references.sh +144 -0
- package/runtime/pi/bin/repo-state.sh +99 -0
- package/runtime/pi/bin/telemetry.sh +391 -0
- package/runtime/pi/bin/worktree-create.sh +118 -0
- package/runtime/pi/bin/worktree-remove.sh +50 -0
- package/runtime/pi/bin/worktree.sh +198 -0
- package/runtime/pi/manifest.json +176 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
|
|
4
|
+
const WIDTH = 72;
|
|
5
|
+
|
|
6
|
+
function usage() {
|
|
7
|
+
console.error(`usage:
|
|
8
|
+
node bin/codex-skill-render.mjs classify-graph [--mode connected|local] [--attempt initial|retry] [file|-]
|
|
9
|
+
node bin/codex-skill-render.mjs activity-card [file|-]
|
|
10
|
+
node bin/codex-skill-render.mjs dashboard-card [file|-]
|
|
11
|
+
node bin/codex-skill-render.mjs handoff-card [file|-]`);
|
|
12
|
+
process.exit(2);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function readInput(file) {
|
|
16
|
+
if (!file || file === '-') {
|
|
17
|
+
return fs.readFileSync(0, 'utf8');
|
|
18
|
+
}
|
|
19
|
+
return fs.readFileSync(file, 'utf8');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function readJson(file) {
|
|
23
|
+
const raw = readInput(file).trim() || '{}';
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(raw);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error(`invalid JSON${file ? ` in ${file}` : ''}: ${error.message}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseFlags(args) {
|
|
33
|
+
const flags = {};
|
|
34
|
+
const rest = [];
|
|
35
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
36
|
+
const arg = args[i];
|
|
37
|
+
if (arg === '--mode' || arg === '--attempt') {
|
|
38
|
+
flags[arg.slice(2)] = args[i + 1];
|
|
39
|
+
i += 1;
|
|
40
|
+
} else {
|
|
41
|
+
rest.push(arg);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { flags, rest };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function asArray(value) {
|
|
48
|
+
return Array.isArray(value) ? value : [];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function firstArray(data, paths) {
|
|
52
|
+
for (const p of paths) {
|
|
53
|
+
const value = p.split('.').reduce((acc, key) => (acc && acc[key] !== undefined ? acc[key] : undefined), data);
|
|
54
|
+
if (Array.isArray(value)) return value;
|
|
55
|
+
}
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function valueOf(item, keys, fallback = '') {
|
|
60
|
+
for (const key of keys) {
|
|
61
|
+
if (item && item[key] !== undefined && item[key] !== null && String(item[key]).trim() !== '') {
|
|
62
|
+
return String(item[key]);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return fallback;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function truncate(value, max) {
|
|
69
|
+
const text = String(value || '').replace(/\s+/g, ' ').trim();
|
|
70
|
+
if (text.length <= max) return text;
|
|
71
|
+
return `${text.slice(0, Math.max(0, max - 3))}...`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function padLine(left, right = '') {
|
|
75
|
+
const inner = WIDTH - 4;
|
|
76
|
+
const r = right ? truncate(right, Math.min(18, inner - 1)) : '';
|
|
77
|
+
const l = truncate(left, inner - (r ? r.length + 1 : 0));
|
|
78
|
+
const space = Math.max(0, inner - l.length - r.length);
|
|
79
|
+
return `│ ${l}${' '.repeat(space)}${r} │`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function topRule() {
|
|
83
|
+
return `┌${'─'.repeat(WIDTH - 2)}┐`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function rule() {
|
|
87
|
+
return `├${'─'.repeat(WIDTH - 2)}┤`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function bottomRule() {
|
|
91
|
+
return `└${'─'.repeat(WIDTH - 2)}┘`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function graphFields(data) {
|
|
95
|
+
return {
|
|
96
|
+
status: data.graph_status || data.graphStatus || data.graph?.status || '',
|
|
97
|
+
reason: data.graph_reason || data.graphReason || data.graph?.reason || '',
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function classifyGraph(data, options = {}) {
|
|
102
|
+
const mode = options.mode || data.mode || 'connected';
|
|
103
|
+
const attempt = options.attempt || 'initial';
|
|
104
|
+
const { status, reason } = graphFields(data);
|
|
105
|
+
|
|
106
|
+
if (mode === 'local') {
|
|
107
|
+
return {
|
|
108
|
+
status: 'local',
|
|
109
|
+
retry: false,
|
|
110
|
+
reason: reason || 'local_mode',
|
|
111
|
+
message: 'Local mode uses memory files as the source of truth.',
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (status === 'connected') {
|
|
116
|
+
return {
|
|
117
|
+
status: 'connected',
|
|
118
|
+
retry: false,
|
|
119
|
+
reason: '',
|
|
120
|
+
message: 'Graph connected.',
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (reason === 'unreachable' && attempt !== 'retry') {
|
|
125
|
+
return {
|
|
126
|
+
status: 'retry',
|
|
127
|
+
retry: true,
|
|
128
|
+
reason,
|
|
129
|
+
message: 'Connected-mode graph was unreachable; retry with network escalation.',
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const finalReason = reason || status || 'offline';
|
|
134
|
+
return {
|
|
135
|
+
status: 'offline',
|
|
136
|
+
retry: false,
|
|
137
|
+
reason: finalReason,
|
|
138
|
+
message: `Graph offline (${finalReason}). Render filesystem fallback.`,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function renderActivity(data) {
|
|
143
|
+
const graph = classifyGraph(data, { mode: data.mode || 'connected', attempt: 'retry' });
|
|
144
|
+
const org = data.org || data.organization || 'Egregore';
|
|
145
|
+
const date = data.date || '';
|
|
146
|
+
const me = data.me?.github || data.me?.name || data.github_username || '';
|
|
147
|
+
const handoffs = firstArray(data, ['handoffs_to_me', 'handoffs', 'pending_handoffs', 'me.handoffs', 'addressed_handoffs']);
|
|
148
|
+
const questions = firstArray(data, ['questions', 'pending_questions', 'me.questions']);
|
|
149
|
+
const prs = asArray(data.prs);
|
|
150
|
+
const mine = firstArray(data, ['local_sessions.my_sessions', 'my_sessions', 'sessions']);
|
|
151
|
+
const team = firstArray(data, ['local_sessions.team_sessions', 'team_sessions', 'team.sessions']);
|
|
152
|
+
|
|
153
|
+
const brand = `${truncate(org, 20).toUpperCase()} EGREGORE ✦ ACTIVITY DASHBOARD`;
|
|
154
|
+
const lines = [topRule(), padLine(brand), padLine([me, date].filter(Boolean).join(' · ')), rule()];
|
|
155
|
+
if (graph.status !== 'connected') {
|
|
156
|
+
lines.push(padLine(`graph: ${graph.status}`, graph.reason));
|
|
157
|
+
lines.push(rule());
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
lines.push(padLine('FOR YOU'));
|
|
161
|
+
if (questions.length === 0 && handoffs.length === 0) {
|
|
162
|
+
lines.push(padLine('nothing pending'));
|
|
163
|
+
} else {
|
|
164
|
+
if (questions.length > 0) {
|
|
165
|
+
lines.push(padLine('QUESTIONS'));
|
|
166
|
+
questions.slice(0, 4).forEach((q) => {
|
|
167
|
+
lines.push(padLine(`● ${valueOf(q, ['from', 'author', 'asker'], 'someone')}: ${valueOf(q, ['topic', 'title', 'question'], 'question')}`));
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
if (handoffs.length > 0) {
|
|
171
|
+
lines.push(padLine('HANDOFFS'));
|
|
172
|
+
handoffs.slice(0, 4).forEach((h) => {
|
|
173
|
+
lines.push(padLine(`○ ${valueOf(h, ['author', 'from'], 'someone')}: ${valueOf(h, ['topic', 'title'], 'handoff')}`));
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
lines.push(rule());
|
|
179
|
+
lines.push(padLine('RECENT WORK'));
|
|
180
|
+
if (mine.length === 0) {
|
|
181
|
+
lines.push(padLine('no recent local sessions found'));
|
|
182
|
+
} else {
|
|
183
|
+
mine.slice(0, 5).forEach((s) => {
|
|
184
|
+
lines.push(padLine(`${valueOf(s, ['date', 'when'], '')} ${valueOf(s, ['topic', 'title', 'branch'], 'session')}`, valueOf(s, ['type', 'status', 'duration'], '')));
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (team.length > 0 || prs.length > 0) {
|
|
189
|
+
lines.push(rule());
|
|
190
|
+
lines.push(padLine('AROUND'));
|
|
191
|
+
team.slice(0, 3).forEach((s) => {
|
|
192
|
+
lines.push(padLine(`${valueOf(s, ['author', 'name'], 'teammate')}: ${valueOf(s, ['topic', 'title', 'branch'], 'session')}`));
|
|
193
|
+
});
|
|
194
|
+
prs.slice(0, 3).forEach((pr) => {
|
|
195
|
+
lines.push(padLine(`#${valueOf(pr, ['number'], '')} ${valueOf(pr, ['title'], 'pull request')}`, valueOf(pr.author || {}, ['login', 'name'], '')));
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
lines.push(rule());
|
|
200
|
+
lines.push(padLine('Focus choices: handoffs, questions, recent work, dashboard, done'));
|
|
201
|
+
lines.push(bottomRule());
|
|
202
|
+
return lines.join('\n');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function humanTopic(session) {
|
|
206
|
+
const topic = valueOf(session, ['topic', 'title'], '');
|
|
207
|
+
if (topic) return topic;
|
|
208
|
+
const branch = valueOf(session, ['branch'], '');
|
|
209
|
+
if (branch && !['develop', 'main', 'master'].includes(branch)) {
|
|
210
|
+
return branch.replace(/^dev\/[^/]+\//, '').replace(/[-_]+/g, ' ');
|
|
211
|
+
}
|
|
212
|
+
return valueOf(session, ['id', 'date'], 'session');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function renderDashboard(data) {
|
|
216
|
+
const graph = classifyGraph(data, { mode: data.mode || 'connected', attempt: 'retry' });
|
|
217
|
+
const org = data.org || 'Egregore';
|
|
218
|
+
const date = data.date || '';
|
|
219
|
+
const me = data.me?.github || data.me?.name || data.github_username || '';
|
|
220
|
+
const current = data.current_session || {};
|
|
221
|
+
const sessions = firstArray(data, ['sessions', 'local_sessions.sessions']);
|
|
222
|
+
const todos = firstArray(data, ['todos', 'open_todos']);
|
|
223
|
+
const quests = firstArray(data, ['quests', 'active_quests']);
|
|
224
|
+
const handoffs = firstArray(data, ['handoffs', 'pending_handoffs']);
|
|
225
|
+
const threads = firstArray(data, ['open_threads', 'threads']);
|
|
226
|
+
const stats = data.stats || {};
|
|
227
|
+
|
|
228
|
+
const lines = [topRule(), padLine('DASHBOARD', [org, me, date].filter(Boolean).join(' · ')), rule()];
|
|
229
|
+
if (graph.status !== 'connected') {
|
|
230
|
+
lines.push(padLine(`graph: ${graph.status}`, graph.reason));
|
|
231
|
+
lines.push(rule());
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
lines.push(padLine(`* ${humanTopic(current) || '(starting...)'}`, 'active'));
|
|
235
|
+
|
|
236
|
+
if (threads.length > 0) {
|
|
237
|
+
lines.push(rule());
|
|
238
|
+
lines.push(padLine('PICK UP WHERE YOU LEFT OFF'));
|
|
239
|
+
threads.slice(0, 5).forEach((thread) => {
|
|
240
|
+
lines.push(padLine(`- ${valueOf(thread, ['text', 'thread', 'title', 'topic'], 'open thread')}`));
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (todos.length > 0) {
|
|
245
|
+
lines.push(rule());
|
|
246
|
+
lines.push(padLine(`OPEN TODOS (${todos.length})`));
|
|
247
|
+
todos.slice(0, 3).forEach((todo, index) => {
|
|
248
|
+
lines.push(padLine(`[${index + 1}] ${valueOf(todo, ['text', 'title'], 'todo')}`, valueOf(todo, ['status'], '')));
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (handoffs.length > 0) {
|
|
253
|
+
lines.push(rule());
|
|
254
|
+
lines.push(padLine(`HANDOFFS (${handoffs.length} pending)`));
|
|
255
|
+
handoffs.slice(0, 3).forEach((handoff) => {
|
|
256
|
+
lines.push(padLine(`${valueOf(handoff, ['author', 'from'], 'someone')}: ${valueOf(handoff, ['topic', 'title'], 'handoff')}`, valueOf(handoff, ['when', 'date'], '')));
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
lines.push(rule());
|
|
261
|
+
lines.push(padLine(`SESSIONS (${data.range_label || 'recent'})`));
|
|
262
|
+
if (sessions.length === 0) {
|
|
263
|
+
lines.push(padLine('No activity recorded yet'));
|
|
264
|
+
} else {
|
|
265
|
+
sessions.slice(0, 5).forEach((session) => {
|
|
266
|
+
lines.push(padLine(`${valueOf(session, ['date', 'when'], '')} ${humanTopic(session)}`, valueOf(session, ['status', 'type'], '')));
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (quests.length > 0) {
|
|
271
|
+
lines.push(rule());
|
|
272
|
+
lines.push(padLine(`QUESTS (${quests.length} active)`));
|
|
273
|
+
quests.slice(0, 3).forEach((quest) => {
|
|
274
|
+
lines.push(padLine(valueOf(quest, ['id', 'quest', 'slug', 'title'], 'quest'), valueOf(quest, ['status', 'summary'], '')));
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
lines.push(rule());
|
|
279
|
+
const total = stats.totalSessions ?? data.local_sessions?.session_count ?? sessions.length;
|
|
280
|
+
const wrapped = stats.wrappedSessions ?? sessions.filter((s) => valueOf(s, ['status', 'type'], '').includes('wrap')).length;
|
|
281
|
+
lines.push(padLine(`${total} sessions · ${wrapped}/${total} wrapped · ${stats.openTodos ?? todos.length} open todos`));
|
|
282
|
+
lines.push(padLine("What's next?"));
|
|
283
|
+
lines.push(bottomRule());
|
|
284
|
+
return lines.join('\n');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function renderHandoff(data) {
|
|
288
|
+
const file = data.file ? `memory/${data.file}` : '';
|
|
289
|
+
const statuses = [
|
|
290
|
+
data.graphStatus ? `graph=${data.graphStatus}` : '',
|
|
291
|
+
data.memoryStatus ? `memory=${data.memoryStatus}` : '',
|
|
292
|
+
data.notifyStatus ? `notify=${data.notifyStatus}` : '',
|
|
293
|
+
].filter(Boolean).join(' · ');
|
|
294
|
+
|
|
295
|
+
const lines = [topRule(), padLine('⇌ HANDOFF', data.author || ''), rule()];
|
|
296
|
+
lines.push(padLine(`Topic: ${data.topic || '(untitled)'}`));
|
|
297
|
+
if (data.recipient) lines.push(padLine(`To: ${data.recipient}`));
|
|
298
|
+
if (file) lines.push(padLine(`Path: ${file}`));
|
|
299
|
+
if (data.artifactUrl) lines.push(padLine(`Link: ${data.artifactUrl}`));
|
|
300
|
+
if (statuses) lines.push(padLine(statuses));
|
|
301
|
+
lines.push(bottomRule());
|
|
302
|
+
return lines.join('\n');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const [command, ...rawArgs] = process.argv.slice(2);
|
|
306
|
+
if (!command) usage();
|
|
307
|
+
|
|
308
|
+
if (command === 'classify-graph') {
|
|
309
|
+
const { flags, rest } = parseFlags(rawArgs);
|
|
310
|
+
const data = readJson(rest[0]);
|
|
311
|
+
console.log(JSON.stringify(classifyGraph(data, flags)));
|
|
312
|
+
} else if (command === 'activity-card') {
|
|
313
|
+
const data = readJson(rawArgs[0]);
|
|
314
|
+
console.log(renderActivity(data));
|
|
315
|
+
} else if (command === 'dashboard-card') {
|
|
316
|
+
const data = readJson(rawArgs[0]);
|
|
317
|
+
console.log(renderDashboard(data));
|
|
318
|
+
} else if (command === 'handoff-card') {
|
|
319
|
+
const data = readJson(rawArgs[0]);
|
|
320
|
+
console.log(renderHandoff(data));
|
|
321
|
+
} else {
|
|
322
|
+
usage();
|
|
323
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
5
|
+
CLAUDE_DIR="$SCRIPT_DIR/.claude/skills"
|
|
6
|
+
CODEX_DIR="$SCRIPT_DIR/.codex/skills"
|
|
7
|
+
CHECK=0
|
|
8
|
+
|
|
9
|
+
NATIVE_SKILLS=(
|
|
10
|
+
activity
|
|
11
|
+
handoff
|
|
12
|
+
wrap
|
|
13
|
+
announce
|
|
14
|
+
harvest
|
|
15
|
+
the-spiral
|
|
16
|
+
dashboard
|
|
17
|
+
deep-reflect
|
|
18
|
+
quest
|
|
19
|
+
invite
|
|
20
|
+
ask
|
|
21
|
+
save
|
|
22
|
+
search
|
|
23
|
+
view
|
|
24
|
+
scroll
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
STRUCTURED_UX_SKILLS=(
|
|
28
|
+
activity
|
|
29
|
+
dashboard
|
|
30
|
+
handoff
|
|
31
|
+
wrap
|
|
32
|
+
todo
|
|
33
|
+
quest
|
|
34
|
+
project
|
|
35
|
+
issue
|
|
36
|
+
test
|
|
37
|
+
qa
|
|
38
|
+
checkup
|
|
39
|
+
infra
|
|
40
|
+
graph-maintain
|
|
41
|
+
telemetry-admin
|
|
42
|
+
waitlist
|
|
43
|
+
hosting
|
|
44
|
+
review-pr
|
|
45
|
+
triage
|
|
46
|
+
eval
|
|
47
|
+
eval-multiagent
|
|
48
|
+
summon
|
|
49
|
+
reflect
|
|
50
|
+
deep-reflect
|
|
51
|
+
archive
|
|
52
|
+
meeting
|
|
53
|
+
ingest-user-interview
|
|
54
|
+
harvest
|
|
55
|
+
tutorial
|
|
56
|
+
onboarding
|
|
57
|
+
emissary
|
|
58
|
+
launch-site
|
|
59
|
+
view
|
|
60
|
+
scroll
|
|
61
|
+
visual-explain
|
|
62
|
+
tui-design
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
GENERATED_MARKER='generated-by: bin/codex-sync-skills.sh'
|
|
66
|
+
|
|
67
|
+
while [ $# -gt 0 ]; do
|
|
68
|
+
case "$1" in
|
|
69
|
+
--check) CHECK=1; shift ;;
|
|
70
|
+
--help|-h)
|
|
71
|
+
echo "usage: bin/codex-sync-skills.sh [--check]"
|
|
72
|
+
exit 0
|
|
73
|
+
;;
|
|
74
|
+
*) echo "unknown option: $1" >&2; exit 2 ;;
|
|
75
|
+
esac
|
|
76
|
+
done
|
|
77
|
+
|
|
78
|
+
is_native() {
|
|
79
|
+
local name="$1"
|
|
80
|
+
local native
|
|
81
|
+
for native in "${NATIVE_SKILLS[@]}"; do
|
|
82
|
+
[ "$name" = "$native" ] && return 0
|
|
83
|
+
done
|
|
84
|
+
return 1
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
is_structured_ux() {
|
|
88
|
+
local name="$1"
|
|
89
|
+
local structured
|
|
90
|
+
for structured in "${STRUCTURED_UX_SKILLS[@]}"; do
|
|
91
|
+
[ "$name" = "$structured" ] && return 0
|
|
92
|
+
done
|
|
93
|
+
return 1
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
skill_description() {
|
|
97
|
+
local file="$1"
|
|
98
|
+
awk '
|
|
99
|
+
NR == 1 && $0 == "---" { in_fm = 1; next }
|
|
100
|
+
in_fm && $0 == "---" { exit }
|
|
101
|
+
in_fm && /^description:/ {
|
|
102
|
+
sub(/^description:[[:space:]]*/, "")
|
|
103
|
+
gsub(/^"|"$/, "")
|
|
104
|
+
print
|
|
105
|
+
exit
|
|
106
|
+
}
|
|
107
|
+
' "$file"
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
yaml_single_quote() {
|
|
111
|
+
local value="$1"
|
|
112
|
+
printf "'%s'" "$(printf '%s' "$value" | sed "s/'/''/g")"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
render_adapter() {
|
|
116
|
+
local name="$1"
|
|
117
|
+
local source="$2"
|
|
118
|
+
local description="$3"
|
|
119
|
+
local structured="$4"
|
|
120
|
+
[ -n "$description" ] || description="Fallback Codex adapter for the Egregore ${name} workflow."
|
|
121
|
+
local description_yaml
|
|
122
|
+
description_yaml="$(yaml_single_quote "$description")"
|
|
123
|
+
cat <<EOF
|
|
124
|
+
---
|
|
125
|
+
name: ${name}
|
|
126
|
+
description: ${description_yaml}
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
<!-- ${GENERATED_MARKER} -->
|
|
130
|
+
|
|
131
|
+
# Egregore ${name} Adapter
|
|
132
|
+
|
|
133
|
+
This is fallback coverage for a long-tail Egregore workflow that has not been
|
|
134
|
+
ported to a hand-written Codex-native skill yet.
|
|
135
|
+
|
|
136
|
+
Use the project shell and filesystem directly. Do not invoke Claude Code
|
|
137
|
+
commands. Translate interactive choices to structured Codex question tooling
|
|
138
|
+
when it is available; otherwise render compact numbered choices with an
|
|
139
|
+
\`Other:\` option and wait for the user.
|
|
140
|
+
|
|
141
|
+
EOF
|
|
142
|
+
if [ "$structured" = "1" ]; then
|
|
143
|
+
cat <<EOF
|
|
144
|
+
## Structured UX parity
|
|
145
|
+
|
|
146
|
+
This workflow has a Claude skill with user-visible structured output. After
|
|
147
|
+
reading \`${source}\`, reproduce the same visible UX in Codex:
|
|
148
|
+
|
|
149
|
+
- Preserve TUI boxes, markdown tables, rich cards, browser artifact rendering,
|
|
150
|
+
exact confirmation blocks, and "no preamble" rules from the source skill.
|
|
151
|
+
- Use the source skill's frame width, section order, labels, status footer,
|
|
152
|
+
and examples as the contract for the final response.
|
|
153
|
+
- Never replace a required box/table/card/artifact view with a prose summary
|
|
154
|
+
unless the user explicitly asks for a summary.
|
|
155
|
+
- When the source says to output a TUI box directly, paste that box as the
|
|
156
|
+
visible response, preferably in a \`text\` fenced block.
|
|
157
|
+
- Never show raw JSON, raw command output, or unformatted script output when
|
|
158
|
+
the source skill requires formatted status or rendered output.
|
|
159
|
+
|
|
160
|
+
EOF
|
|
161
|
+
fi
|
|
162
|
+
cat <<EOF
|
|
163
|
+
1. Read \`${source}\` for the workflow details.
|
|
164
|
+
2. Run the referenced \`bin/\` scripts directly from Codex.
|
|
165
|
+
3. Treat graph, publish, and notify steps as best-effort unless that workflow
|
|
166
|
+
explicitly says they are required.
|
|
167
|
+
4. Keep local-mode behavior filesystem-first and avoid graph or notification
|
|
168
|
+
calls when \`egregore.json\` declares \`"mode": "local"\`.
|
|
169
|
+
5. Never call the deprecated \`egregore-handoff\` CLI for Egregore project
|
|
170
|
+
handoffs.
|
|
171
|
+
EOF
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
[ -d "$CLAUDE_DIR" ] || { echo "missing Claude skills directory: $CLAUDE_DIR" >&2; exit 1; }
|
|
175
|
+
mkdir -p "$CODEX_DIR"
|
|
176
|
+
|
|
177
|
+
changed=0
|
|
178
|
+
native_present=0
|
|
179
|
+
adapter_present=0
|
|
180
|
+
|
|
181
|
+
for native in "${NATIVE_SKILLS[@]}"; do
|
|
182
|
+
if [ -f "$CODEX_DIR/$native/SKILL.md" ]; then
|
|
183
|
+
native_present=$((native_present + 1))
|
|
184
|
+
else
|
|
185
|
+
echo "missing native Codex skill: $native" >&2
|
|
186
|
+
changed=1
|
|
187
|
+
fi
|
|
188
|
+
done
|
|
189
|
+
|
|
190
|
+
while IFS= read -r source_file; do
|
|
191
|
+
name="$(basename "$(dirname "$source_file")")"
|
|
192
|
+
is_native "$name" && continue
|
|
193
|
+
|
|
194
|
+
target_dir="$CODEX_DIR/$name"
|
|
195
|
+
target_file="$target_dir/SKILL.md"
|
|
196
|
+
if [ -f "$target_file" ] && ! grep -q "$GENERATED_MARKER" "$target_file" 2>/dev/null; then
|
|
197
|
+
continue
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
description="$(skill_description "$source_file")"
|
|
201
|
+
rel_source=".claude/skills/$name/SKILL.md"
|
|
202
|
+
structured=0
|
|
203
|
+
is_structured_ux "$name" && structured=1
|
|
204
|
+
tmp="$(mktemp -t codex-skill-adapter-XXXXXX)"
|
|
205
|
+
render_adapter "$name" "$rel_source" "$description" "$structured" > "$tmp"
|
|
206
|
+
|
|
207
|
+
if [ ! -f "$target_file" ] || ! cmp -s "$tmp" "$target_file"; then
|
|
208
|
+
changed=1
|
|
209
|
+
if [ "$CHECK" = "1" ]; then
|
|
210
|
+
echo "adapter out of date: $name"
|
|
211
|
+
else
|
|
212
|
+
mkdir -p "$target_dir"
|
|
213
|
+
cp "$tmp" "$target_file"
|
|
214
|
+
echo "generated adapter: $name"
|
|
215
|
+
fi
|
|
216
|
+
fi
|
|
217
|
+
rm -f "$tmp"
|
|
218
|
+
done < <(find "$CLAUDE_DIR" -mindepth 2 -maxdepth 2 -name SKILL.md | sort)
|
|
219
|
+
|
|
220
|
+
adapter_present="$({ grep -rl "$GENERATED_MARKER" "$CODEX_DIR"/*/SKILL.md 2>/dev/null || true; } | wc -l | tr -d ' ')"
|
|
221
|
+
|
|
222
|
+
if [ "$CHECK" = "1" ] && [ "$changed" -ne 0 ]; then
|
|
223
|
+
echo "codex skills out of sync (native: $native_present/${#NATIVE_SKILLS[@]}, adapters: $adapter_present)" >&2
|
|
224
|
+
exit 1
|
|
225
|
+
fi
|
|
226
|
+
|
|
227
|
+
echo "codex skills synced (native: $native_present/${#NATIVE_SKILLS[@]}, adapters: $adapter_present)"
|