@neikyun/ciel 6.0.2 → 6.0.4
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/dist/plugin/index.js +509 -0
- package/assets/platforms/opencode/.opencode/commands/ciel-update.md +43 -11
- package/assets/platforms/opencode/.opencode/plugins/ciel.js +509 -0
- package/dist/cli/check.d.ts.map +1 -1
- package/dist/cli/check.js +18 -9
- package/dist/cli/check.js.map +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +8 -4
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/opencode.d.ts.map +1 -1
- package/dist/cli/opencode.js +47 -11
- package/dist/cli/opencode.js.map +1 -1
- package/package.json +2 -2
- package/scripts/postinstall.cjs +16 -7
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Ciel -- OpenCode plugin (v6.0.0)
|
|
3
|
+
// Full 16-step pipeline: DOCS -> QUOI -> ASK -> AVEC QUOI -> DIVERGE
|
|
4
|
+
// -> RECHERCHE -> SECURITE -> CODEBASE -> EVALUER -> ASK2
|
|
5
|
+
// -> FAIRE -> ADR -> RELIRE -> PROUVER -> MEMOIRE -> META
|
|
6
|
+
//
|
|
7
|
+
// Injection model:
|
|
8
|
+
// - shell.env -> inject CIEL_SESSION_ID, CIEL_DEPTH, CIEL_MODE
|
|
9
|
+
// - experimental.chat.system.transform -> CIEL WORKFLOW v6 + overlay + state
|
|
10
|
+
// - experimental.chat.messages.transform -> depth classification
|
|
11
|
+
// - session.* events -> tracking, META-CRITIQUER, RELIRE reminders
|
|
12
|
+
// - tool.execute.before -> FAIRE gates reminder + critical file detection
|
|
13
|
+
// - tool.execute.after -> file tracking + RELIRE trigger + map update
|
|
14
|
+
// - experimental.session.compacting -> persist learnings + map
|
|
15
|
+
// - tool helper -> custom ciel-status tool
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
const plugin_1 = require("@opencode-ai/plugin");
|
|
18
|
+
const fs_1 = require("fs");
|
|
19
|
+
const path_1 = require("path");
|
|
20
|
+
// ----- Constants -----
|
|
21
|
+
const CODE_EXT_RE = /\.(kt|java|ts|tsx|js|jsx|py|go|rs|rb|php|cs|cpp|c|swift|scala|vue|svelte|sql)$/i;
|
|
22
|
+
const TEST_FILE_RE = /(\.test\.|\.spec\.|_test\.|_spec\.)(ts|tsx|js|jsx|py|go|rs|rb|php|cs|cpp|c|swift|scala|vue|svelte)$/i;
|
|
23
|
+
const CRITICAL_FILE_RE = /(auth|Auth|security|Security|Route|Service|Controller|Repository|Gateway|Middleware|Proxy|Token|Session|Password|Secret|Payment|Account|Credential)/;
|
|
24
|
+
const CRITICAL_KEYWORD_RE = /\b(auth|authenti|author|jwt|oauth|password|secret|token|session|payment|credit.card|migration.*schema|2fa|mfa|encryption|credential|cookie.*security)\b/i;
|
|
25
|
+
const TRIVIAL_KEYWORD_RE = /\b(rename|typo|copyright|comment|readme|1-line|one.line|fix.typo|spelling)\b/i;
|
|
26
|
+
const CIEL_DIR = ".ciel";
|
|
27
|
+
const MAP_FILE = (0, path_1.join)(CIEL_DIR, "map.json");
|
|
28
|
+
const PARKING_FILE = (0, path_1.join)(CIEL_DIR, "parking.md");
|
|
29
|
+
const LEARNINGS_FILE = (0, path_1.join)(CIEL_DIR, "learnings.md");
|
|
30
|
+
const EXPLORATION_FLAG = (0, path_1.join)(CIEL_DIR, "exploration.active");
|
|
31
|
+
const MEMORY_FILE = (0, path_1.join)(CIEL_DIR, "memory.json");
|
|
32
|
+
// ----- V5 WORKFLOW INJECTION -----
|
|
33
|
+
const CIEL_WORKFLOW_INSTRUCTION = `
|
|
34
|
+
Every response MUST start with depth classification. Then follow the matching pipeline.
|
|
35
|
+
|
|
36
|
+
CLASSIFY: Trivial / Standard / Critical / Spike
|
|
37
|
+
|
|
38
|
+
| Depth | Pipeline |
|
|
39
|
+
|-------|----------|
|
|
40
|
+
| Standard/Critical | DOCS -> QUOI -> ASK -> AVEC QUOI -> DIVERGE -> RECHERCHE -> SECURITE -> CODEBASE -> EVALUER -> ASK2 -> FAIRE -> ADR -> RELIRE -> PROUVER -> MEMOIRE -> META |
|
|
41
|
+
| Trivial | QUOI -> FAIRE -> META |
|
|
42
|
+
| Spike | QUOI -> ASK -> AVEC QUOI -> DIVERGE -> FAIRE (relaxed) -> META |
|
|
43
|
+
|
|
44
|
+
USE the question tool for ASK/ASK2. NEVER skip steps. NEVER code on assumptions.
|
|
45
|
+
`;
|
|
46
|
+
const FAIRE_BEFORE_REMINDER = `
|
|
47
|
+
[CIEL FAIRE GATES -- BEFORE WRITE/EDIT]
|
|
48
|
+
Before executing this write/edit, verify:
|
|
49
|
+
1. TEST-FIRST (RED): Have you written tests FIRST? If this is source code, a corresponding test file must exist or be created first.
|
|
50
|
+
2. ALTERNATIVES: Can you justify X over Y? (comment or commit message)
|
|
51
|
+
3. IDIOMATIC: Are you using the framework's idiomatic pattern? If bypassing, justify why.
|
|
52
|
+
4. QUALITY: complexity < 15, nesting < 4, functions < 50 lines
|
|
53
|
+
5. REMOVAL: If deleting code -- who uses it? What replaces it? What degrades?
|
|
54
|
+
6. BOY-SCOUT: Did you leave the code better than you found it?
|
|
55
|
+
`;
|
|
56
|
+
const META_CRITIQUER = `
|
|
57
|
+
[CIEL META-CRITIQUER -- 30s POST-TASK REFLECTION]
|
|
58
|
+
After completing the task, reflect on:
|
|
59
|
+
(1) Depth match -- etait-ce Trivial/Standard/Critical/Spike correct ?
|
|
60
|
+
(2) Failure mode -- nouveau mode d'echec decouvert ?
|
|
61
|
+
(3) User correction -- l'utilisateur a-t-il corrige quelque chose ? -> persist dans learnings
|
|
62
|
+
(4) Stale branches -- branches a nettoyer ?
|
|
63
|
+
(5) Uncovered issues -- problemes non resolus ?
|
|
64
|
+
(6) Context health -- suggerer /compact si > 50% ?
|
|
65
|
+
(7) Dead code -- code mort introduit ?
|
|
66
|
+
(8) Map update -- la carte du projet (.ciel/map.json) est-elle a jour ?
|
|
67
|
+
(9) Parking -- y a-t-il des decouvertes fortuites a noter dans .ciel/parking.md ?
|
|
68
|
+
(10) Boy-scout -- le code est-il meilleur qu'avant ?
|
|
69
|
+
`;
|
|
70
|
+
// ----- Helpers -----
|
|
71
|
+
function getTestPathForSource(sourcePath) {
|
|
72
|
+
const base = (0, path_1.basename)(sourcePath);
|
|
73
|
+
const dir = (0, path_1.dirname)(sourcePath);
|
|
74
|
+
const nameWithoutExt = base.replace(/\.[^.]+$/, "");
|
|
75
|
+
const ext = base.match(/\.[^.]+$/)?.[0] ?? "";
|
|
76
|
+
const candidates = [
|
|
77
|
+
(0, path_1.join)(dir, `${nameWithoutExt}.test${ext}`),
|
|
78
|
+
(0, path_1.join)(dir, `${nameWithoutExt}.spec${ext}`),
|
|
79
|
+
(0, path_1.join)(dir, `${nameWithoutExt}_test${ext}`),
|
|
80
|
+
(0, path_1.join)(dir, `${nameWithoutExt}_spec${ext}`),
|
|
81
|
+
(0, path_1.join)(dir, "__tests__", `${nameWithoutExt}${ext}`),
|
|
82
|
+
(0, path_1.join)(dir, "test", `${nameWithoutExt}${ext}`),
|
|
83
|
+
(0, path_1.join)(dir, "tests", `${nameWithoutExt}${ext}`),
|
|
84
|
+
];
|
|
85
|
+
if (ext === ".ts" || ext === ".tsx") {
|
|
86
|
+
candidates.push((0, path_1.join)(dir, `${nameWithoutExt}.test.js`));
|
|
87
|
+
candidates.push((0, path_1.join)(dir, `${nameWithoutExt}.spec.js`));
|
|
88
|
+
}
|
|
89
|
+
return candidates;
|
|
90
|
+
}
|
|
91
|
+
function sourceFileHasTest(sourcePath) {
|
|
92
|
+
const candidates = getTestPathForSource(sourcePath);
|
|
93
|
+
for (const candidate of candidates) {
|
|
94
|
+
if ((0, fs_1.existsSync)(candidate))
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
function isTestFile(filePath) {
|
|
100
|
+
return TEST_FILE_RE.test(filePath);
|
|
101
|
+
}
|
|
102
|
+
function isSourceFile(filePath) {
|
|
103
|
+
return CODE_EXT_RE.test(filePath) && !isTestFile(filePath);
|
|
104
|
+
}
|
|
105
|
+
function isSpikeMode() {
|
|
106
|
+
return (0, fs_1.existsSync)(EXPLORATION_FLAG);
|
|
107
|
+
}
|
|
108
|
+
function ensureCielDir() {
|
|
109
|
+
if (!(0, fs_1.existsSync)(CIEL_DIR)) {
|
|
110
|
+
(0, fs_1.mkdirSync)(CIEL_DIR, { recursive: true });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function writeParkingEntry(entry) {
|
|
114
|
+
ensureCielDir();
|
|
115
|
+
const timestamp = new Date().toISOString().split("T")[0];
|
|
116
|
+
const formatted = `- [${timestamp}] ${entry}\n`;
|
|
117
|
+
try {
|
|
118
|
+
const existing = (0, fs_1.existsSync)(PARKING_FILE) ? (0, fs_1.readFileSync)(PARKING_FILE, "utf-8") : "";
|
|
119
|
+
const header = "# Ciel Parking Lot -- Decouvertes fortuites\n\n";
|
|
120
|
+
const content = existing.startsWith("#") ? existing : header + existing;
|
|
121
|
+
(0, fs_1.writeFileSync)(PARKING_FILE, content + formatted, "utf-8");
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// silent
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// ----- Plugin -----
|
|
128
|
+
const ciel = async ({ client }) => {
|
|
129
|
+
const writtenFiles = new Set();
|
|
130
|
+
const MAX_TRACKED_FILES = 100;
|
|
131
|
+
let relireSticky = false;
|
|
132
|
+
let lastDepthHint = null;
|
|
133
|
+
let overlayContent = null;
|
|
134
|
+
let faireBlocked = null;
|
|
135
|
+
let sessionId = "unknown";
|
|
136
|
+
let taskCount = 0;
|
|
137
|
+
let readDocsAttempted = false;
|
|
138
|
+
let askWindowUsed = false;
|
|
139
|
+
return {
|
|
140
|
+
// ----- CUSTOM TOOLS -----
|
|
141
|
+
tool: {
|
|
142
|
+
"ciel-status": (0, plugin_1.tool)({
|
|
143
|
+
description: "Shows current Ciel session state: depth classification, files changed, RELIRE status, FAIRE gate state, spike mode. Use when user asks 'what is the Ciel status' or 'show Ciel state'.",
|
|
144
|
+
args: {},
|
|
145
|
+
async execute(_args, _context) {
|
|
146
|
+
const changed = Array.from(writtenFiles);
|
|
147
|
+
return JSON.stringify({
|
|
148
|
+
sessionId,
|
|
149
|
+
depthHint: lastDepthHint,
|
|
150
|
+
filesChanged: changed.length,
|
|
151
|
+
files: changed.slice(0, 10),
|
|
152
|
+
relireRequired: relireSticky,
|
|
153
|
+
spikeMode: isSpikeMode(),
|
|
154
|
+
taskCount,
|
|
155
|
+
askWindowUsed,
|
|
156
|
+
readDocsAttempted,
|
|
157
|
+
faireBlocked: faireBlocked ? {
|
|
158
|
+
file: faireBlocked.filePath,
|
|
159
|
+
gate: faireBlocked.gate,
|
|
160
|
+
} : null,
|
|
161
|
+
overlayLoaded: overlayContent != null,
|
|
162
|
+
}, null, 2);
|
|
163
|
+
},
|
|
164
|
+
}),
|
|
165
|
+
},
|
|
166
|
+
// ----- SHELL ENV -----
|
|
167
|
+
"shell.env": async (_input, output) => {
|
|
168
|
+
output.env.CIEL_SESSION_ID = sessionId;
|
|
169
|
+
output.env.CIEL_DEPTH = lastDepthHint ?? "unclassified";
|
|
170
|
+
output.env.CIEL_MODE = isSpikeMode() ? "spike" : "standard";
|
|
171
|
+
},
|
|
172
|
+
// ----- EVENTS -----
|
|
173
|
+
event: async ({ event }) => {
|
|
174
|
+
// Log all event types for debugging session ID detection
|
|
175
|
+
if (event.type && !event.type.startsWith("tool.") && event.type !== "session.diff") {
|
|
176
|
+
await client.app.log({
|
|
177
|
+
body: { service: "ciel", level: "debug", message: `Event: ${event.type}` },
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (event.type === "session.created" || event.type === "session.updated" || event.type === "session.status") {
|
|
181
|
+
// Try multiple locations for session ID
|
|
182
|
+
const evt = event;
|
|
183
|
+
const rawId = evt?.info?.id ?? evt?.sessionID ?? evt?.sessionId ?? evt?.id ?? evt?.session?.id ?? `s-${Date.now().toString(36)}`;
|
|
184
|
+
sessionId = typeof rawId === "string" ? rawId.slice(0, 8) : "unknown";
|
|
185
|
+
taskCount = 0;
|
|
186
|
+
}
|
|
187
|
+
// Always ensure .ciel/ directory exists (runs on ANY session event)
|
|
188
|
+
if (event.type === "session.created" || event.type === "session.updated" || event.type === "session.status" || event.type === "session.diff") {
|
|
189
|
+
// Initialize .ciel/ directory if missing
|
|
190
|
+
ensureCielDir();
|
|
191
|
+
if (!(0, fs_1.existsSync)(MAP_FILE)) {
|
|
192
|
+
(0, fs_1.writeFileSync)(MAP_FILE, JSON.stringify({ modules: [], lastUpdated: new Date().toISOString() }, null, 2), "utf-8");
|
|
193
|
+
}
|
|
194
|
+
if (!(0, fs_1.existsSync)(MEMORY_FILE)) {
|
|
195
|
+
(0, fs_1.writeFileSync)(MEMORY_FILE, "{}", "utf-8");
|
|
196
|
+
}
|
|
197
|
+
if (!(0, fs_1.existsSync)(PARKING_FILE)) {
|
|
198
|
+
(0, fs_1.writeFileSync)(PARKING_FILE, "# Ciel Parking Lot -- Decouvertes fortuites\n\n", "utf-8");
|
|
199
|
+
}
|
|
200
|
+
await client.app.log({
|
|
201
|
+
body: { service: "ciel", level: "info", message: `Session ${sessionId} started` },
|
|
202
|
+
});
|
|
203
|
+
// Load overlay
|
|
204
|
+
if ((0, fs_1.existsSync)("./ciel-overlay.md")) {
|
|
205
|
+
try {
|
|
206
|
+
const rawOverlay = (0, fs_1.readFileSync)("./ciel-overlay.md", "utf-8");
|
|
207
|
+
overlayContent = rawOverlay.replace(/##\s*\S*sensitive[:\s]*true\S*\s*\n([\s\S]*?)(?=\n##\s|\n*$)/gi, "## [REDACTED -- sensitive section]\n");
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// silent
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
writtenFiles.clear();
|
|
214
|
+
relireSticky = false;
|
|
215
|
+
lastDepthHint = null;
|
|
216
|
+
faireBlocked = null;
|
|
217
|
+
readDocsAttempted = false;
|
|
218
|
+
askWindowUsed = false;
|
|
219
|
+
}
|
|
220
|
+
if (event.type === "session.diff") {
|
|
221
|
+
const diffs = event.diff ?? [];
|
|
222
|
+
for (const fileDiff of diffs) {
|
|
223
|
+
const path = fileDiff?.path ?? "";
|
|
224
|
+
if (CODE_EXT_RE.test(path)) {
|
|
225
|
+
if (writtenFiles.size >= MAX_TRACKED_FILES) {
|
|
226
|
+
const firstKey = writtenFiles.values().next().value;
|
|
227
|
+
writtenFiles.delete(firstKey);
|
|
228
|
+
}
|
|
229
|
+
writtenFiles.add(path);
|
|
230
|
+
if (writtenFiles.size >= 5 || CRITICAL_FILE_RE.test(path)) {
|
|
231
|
+
relireSticky = true;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (event.type === "session.idle") {
|
|
237
|
+
taskCount++;
|
|
238
|
+
lastDepthHint = `CIEL STOP -- META-CRITIQUER: (1) depth match? (2) failure mode? (3) user correction -> learnings? (4) stale branches? (8) map update? (9) parking note?`;
|
|
239
|
+
relireSticky = true;
|
|
240
|
+
faireBlocked = null;
|
|
241
|
+
}
|
|
242
|
+
if (event.type === "session.deleted") {
|
|
243
|
+
const rawId = event.sessionID ?? event.info?.id ?? "unknown";
|
|
244
|
+
const sid = typeof rawId === "string" ? rawId.slice(0, 8) : "unknown";
|
|
245
|
+
const isChild = event.parentSessionId != null;
|
|
246
|
+
await client.app.log({
|
|
247
|
+
body: { service: "ciel", level: "info", message: `Session ${sid} deleted${isChild ? " (subagent child)" : ""}` },
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
if (event.type === "session.error") {
|
|
251
|
+
const errorName = event.error?.name ?? "UnknownError";
|
|
252
|
+
const errorMessage = event.error?.message ?? "";
|
|
253
|
+
if (errorName === "ProviderAuthError" || errorName === "MessageAbortedError") {
|
|
254
|
+
await client.app.log({
|
|
255
|
+
body: { service: "ciel", level: "error", message: `${errorName}: ${errorMessage}` },
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (event.type === "session.compacted") {
|
|
260
|
+
await client.app.log({
|
|
261
|
+
body: { service: "ciel", level: "info", message: `Session ${sessionId} compacted -- state preserved` },
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
// ----- SYSTEM TRANSFORM -----
|
|
266
|
+
"experimental.chat.system.transform": async (_input, output) => {
|
|
267
|
+
if (!Array.isArray(output?.system))
|
|
268
|
+
return;
|
|
269
|
+
// Mandatory workflow injection (first -- highest priority)
|
|
270
|
+
output.system.push(CIEL_WORKFLOW_INSTRUCTION);
|
|
271
|
+
// Overlay injection
|
|
272
|
+
if (overlayContent) {
|
|
273
|
+
output.system.push(`Project Overlay:\n${overlayContent}`);
|
|
274
|
+
}
|
|
275
|
+
// Load .ciel/map.json if it exists
|
|
276
|
+
if ((0, fs_1.existsSync)(MAP_FILE)) {
|
|
277
|
+
try {
|
|
278
|
+
const mapContent = (0, fs_1.readFileSync)(MAP_FILE, "utf-8");
|
|
279
|
+
output.system.push(`Project Map (.ciel/map.json):\n${mapContent}`);
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
// silent
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
// Load .ciel/memory.json if it exists
|
|
286
|
+
if ((0, fs_1.existsSync)(MEMORY_FILE)) {
|
|
287
|
+
try {
|
|
288
|
+
const memoryContent = (0, fs_1.readFileSync)(MEMORY_FILE, "utf-8");
|
|
289
|
+
output.system.push(`Session Memory (.ciel/memory.json):\n${memoryContent}`);
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
// silent
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
// SPIKE mode indicator
|
|
296
|
+
if (isSpikeMode()) {
|
|
297
|
+
output.system.push("[CIEL SPIKE MODE] Exploration/prototype mode active. Quality gates are ASSOUPLIES.\n" +
|
|
298
|
+
"This code is experimental. FIXME/TODO markers required. Must be refactored properly after.\n" +
|
|
299
|
+
"To exit spike mode, remove .ciel/exploration.active");
|
|
300
|
+
}
|
|
301
|
+
// Depth hint
|
|
302
|
+
if (lastDepthHint) {
|
|
303
|
+
output.system.push(lastDepthHint);
|
|
304
|
+
}
|
|
305
|
+
// META-CRITIQUER always injected
|
|
306
|
+
output.system.push(META_CRITIQUER);
|
|
307
|
+
// FAIRE gate blocked
|
|
308
|
+
if (faireBlocked) {
|
|
309
|
+
output.system.push(`[CIEL FAIRE GATE TRIGGERED] You just wrote ${faireBlocked.filePath} without a corresponding test file.\n\n` +
|
|
310
|
+
`This means you skipped the Ciel workflow. You MUST now:\n` +
|
|
311
|
+
`1. Classify depth (Trivial/Standard/Critical/Spike)\n` +
|
|
312
|
+
`2. Follow the pipeline: DOCS -> QUOI -> ASK -> AVEC QUOI -> ... -> FAIRE -> RELIRE -> PROUVER -> MEMOIRE -> META\n` +
|
|
313
|
+
`3. Dispatch subagents if required (@ciel-researcher, @ciel-explorer)\n` +
|
|
314
|
+
`4. Write the test file FIRST, then implement\n\n` +
|
|
315
|
+
`Candidates checked: ${faireBlocked.candidates.slice(0, 3).join(", ")}\n\n` +
|
|
316
|
+
`Do NOT continue writing source code until tests exist. Follow the full Ciel pipeline.`);
|
|
317
|
+
}
|
|
318
|
+
// RELIRE sticky notice
|
|
319
|
+
if (relireSticky) {
|
|
320
|
+
const changed = Array.from(writtenFiles);
|
|
321
|
+
output.system.push(`[CIEL RELIRE REQUIRED] ${changed.length} files changed. Dispatch @ciel-critic MODE=RELIRE -- 3 RISQUES + FIX/ACCEPT/DEFER.`);
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
// ----- MESSAGES TRANSFORM (depth classification) -----
|
|
325
|
+
// Read the most recent user message and classify depth.
|
|
326
|
+
// Does NOT modify the messages array -- splicing synthetic messages
|
|
327
|
+
// causes "U.parts.length undefined" crashes in the OpenCode SDK
|
|
328
|
+
// (the injected message lacks the expected message shape).
|
|
329
|
+
// Depth hints are injected via experimental.chat.system.transform instead.
|
|
330
|
+
"experimental.chat.messages.transform": async (_input, output) => {
|
|
331
|
+
const msgs = output?.messages;
|
|
332
|
+
if (!Array.isArray(msgs) || msgs.length === 0)
|
|
333
|
+
return;
|
|
334
|
+
// Find the most recent user text part.
|
|
335
|
+
let prompt = "";
|
|
336
|
+
for (let i = msgs.length - 1; i >= 0 && !prompt; i--) {
|
|
337
|
+
const m = msgs[i];
|
|
338
|
+
if (m?.info?.role !== "user")
|
|
339
|
+
continue;
|
|
340
|
+
const parts = m?.parts;
|
|
341
|
+
if (!Array.isArray(parts))
|
|
342
|
+
continue;
|
|
343
|
+
for (let j = parts.length - 1; j >= 0; j--) {
|
|
344
|
+
const p = parts[j];
|
|
345
|
+
if (p?.type === "text" && typeof p.text === "string") {
|
|
346
|
+
prompt = p.text;
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (!prompt)
|
|
352
|
+
return;
|
|
353
|
+
let depth = null;
|
|
354
|
+
let reason = "";
|
|
355
|
+
if (CRITICAL_KEYWORD_RE.test(prompt)) {
|
|
356
|
+
depth = "Critical";
|
|
357
|
+
reason = "auth/security/payment keyword detected";
|
|
358
|
+
}
|
|
359
|
+
else if (TRIVIAL_KEYWORD_RE.test(prompt)) {
|
|
360
|
+
depth = "Trivial";
|
|
361
|
+
reason = "rename/typo/docs keyword detected";
|
|
362
|
+
}
|
|
363
|
+
lastDepthHint = depth
|
|
364
|
+
? `[CIEL] Depth: ${depth} (${reason}). Route the pipeline accordingly.`
|
|
365
|
+
: null;
|
|
366
|
+
},
|
|
367
|
+
// ----- COMPACTING (cross-session memory -- persist automatically) -----
|
|
368
|
+
"experimental.session.compacting": async (_input, output) => {
|
|
369
|
+
// Persist .ciel/memory.json with current state
|
|
370
|
+
try {
|
|
371
|
+
ensureCielDir();
|
|
372
|
+
const memory = {
|
|
373
|
+
sessionId,
|
|
374
|
+
depthHint: lastDepthHint,
|
|
375
|
+
filesChanged: Array.from(writtenFiles).slice(-20),
|
|
376
|
+
taskCount,
|
|
377
|
+
timestamp: new Date().toISOString(),
|
|
378
|
+
};
|
|
379
|
+
(0, fs_1.writeFileSync)(MEMORY_FILE, JSON.stringify(memory, null, 2), "utf-8");
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
// silent
|
|
383
|
+
}
|
|
384
|
+
// Inject context for the LLM to update learnings, map, and parking
|
|
385
|
+
output.context.push("CIEL PRE-COMPACT -- Persist if needed: (1) user corrections -> .ciel/learnings.md, " +
|
|
386
|
+
"(2) project map updates -> .ciel/map.json, " +
|
|
387
|
+
"(3) fortuitous discoveries -> .ciel/parking.md. " +
|
|
388
|
+
"Memory already saved at .ciel/memory.json");
|
|
389
|
+
},
|
|
390
|
+
// ----- BEFORE HOOK -- FAIRE gates -----
|
|
391
|
+
"tool.execute.before": async (input, output) => {
|
|
392
|
+
if (!["write", "edit"].includes(input.tool))
|
|
393
|
+
return;
|
|
394
|
+
const filePath = output?.args?.filePath ?? "";
|
|
395
|
+
if (!filePath || !CODE_EXT_RE.test(filePath))
|
|
396
|
+
return;
|
|
397
|
+
// Skip for the plugin itself and test files
|
|
398
|
+
if (filePath.includes("ciel.ts") || isTestFile(filePath))
|
|
399
|
+
return;
|
|
400
|
+
// Gate 1: TEST-FIRST (RED) -- only block if NOT in SPIKE mode
|
|
401
|
+
if (isSourceFile(filePath) && !sourceFileHasTest(filePath) && !isSpikeMode()) {
|
|
402
|
+
const testCandidates = getTestPathForSource(filePath);
|
|
403
|
+
faireBlocked = { filePath, gate: "test-first", candidates: testCandidates };
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
faireBlocked = null;
|
|
407
|
+
}
|
|
408
|
+
// Gate 2: CRITICAL FILE WARNING
|
|
409
|
+
if (CRITICAL_FILE_RE.test(filePath)) {
|
|
410
|
+
await client.app.log({
|
|
411
|
+
body: { service: "ciel", level: "warn", message: `CRITICAL FILE: ${filePath} -- stride-analyzer + security-regression-check required` },
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
// Gate 3: PARKING LOT -- detect if this is a tangential discovery
|
|
415
|
+
if (filePath.includes("parking") || filePath.includes("FIXME") || filePath.includes("TODO")) {
|
|
416
|
+
writeParkingEntry(`Tangential file noted during task: ${filePath}`);
|
|
417
|
+
}
|
|
418
|
+
// Inject FAIRE reminder into the tool output
|
|
419
|
+
const faireReminder = FAIRE_BEFORE_REMINDER.trim();
|
|
420
|
+
if (typeof output?.output === "string") {
|
|
421
|
+
output.output = faireReminder + "\n" + output.output;
|
|
422
|
+
}
|
|
423
|
+
else if (output) {
|
|
424
|
+
output.output = faireReminder;
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
// ----- AFTER HOOK -- file tracking + map update -----
|
|
428
|
+
"tool.execute.after": async (input, output) => {
|
|
429
|
+
const toolName = input?.tool ?? "";
|
|
430
|
+
// Track ASK window usage for any question tool call
|
|
431
|
+
if (toolName === "question") {
|
|
432
|
+
askWindowUsed = true;
|
|
433
|
+
return; // question tool has no file path, nothing else to do
|
|
434
|
+
}
|
|
435
|
+
// Only process write/edit tools for file tracking
|
|
436
|
+
if (!["write", "edit"].includes(toolName))
|
|
437
|
+
return;
|
|
438
|
+
const filePath = output?.metadata?.filepath ??
|
|
439
|
+
output?.metadata?.filediff?.file ??
|
|
440
|
+
output?.args?.filePath ??
|
|
441
|
+
"";
|
|
442
|
+
if (!filePath || !CODE_EXT_RE.test(filePath))
|
|
443
|
+
return;
|
|
444
|
+
if (writtenFiles.size >= MAX_TRACKED_FILES) {
|
|
445
|
+
const firstKey = writtenFiles.values().next().value;
|
|
446
|
+
writtenFiles.delete(firstKey);
|
|
447
|
+
}
|
|
448
|
+
writtenFiles.add(filePath);
|
|
449
|
+
if (writtenFiles.size >= 5 || CRITICAL_FILE_RE.test(filePath)) {
|
|
450
|
+
relireSticky = true;
|
|
451
|
+
}
|
|
452
|
+
const isCritical = CRITICAL_FILE_RE.test(filePath);
|
|
453
|
+
const spike = isSpikeMode();
|
|
454
|
+
const reminder = isCritical
|
|
455
|
+
? `\n\n[CIEL CRITIQUE] ${filePath} -- FAIRE gates + stride-analyzer + test-first (RED). Dispatch @ciel-critic MODE=RELIRE.`
|
|
456
|
+
: spike
|
|
457
|
+
? `\n\n[CIEL SPIKE] ${filePath} -- gates assouplies. Marquer comme experimental (FIXME/TODO).`
|
|
458
|
+
: `\n\n[CIEL] ${filePath} -- FAIRE gates: alternatives, idiomatic, test-first, boy-scout.`;
|
|
459
|
+
if (typeof output?.output === "string") {
|
|
460
|
+
output.output += reminder;
|
|
461
|
+
}
|
|
462
|
+
else if (output) {
|
|
463
|
+
output.output = reminder.trimStart();
|
|
464
|
+
}
|
|
465
|
+
// Track DOCS phase attempt
|
|
466
|
+
if (!readDocsAttempted && (filePath.endsWith("README.md") || filePath.endsWith("AGENTS.md") || filePath.endsWith("CLAUDE.md") || filePath.includes("ciel-overlay") || filePath.endsWith("docs/"))) {
|
|
467
|
+
readDocsAttempted = true;
|
|
468
|
+
}
|
|
469
|
+
// Update .ciel/map.json with modules discovered during exploration
|
|
470
|
+
if (filePath.endsWith(".ts") || filePath.endsWith(".tsx") || filePath.endsWith(".js") || filePath.endsWith(".py") || filePath.endsWith(".go") || filePath.endsWith(".rs")) {
|
|
471
|
+
try {
|
|
472
|
+
ensureCielDir();
|
|
473
|
+
let map = { modules: [], lastUpdated: new Date().toISOString() };
|
|
474
|
+
if ((0, fs_1.existsSync)(MAP_FILE)) {
|
|
475
|
+
map = JSON.parse((0, fs_1.readFileSync)(MAP_FILE, "utf-8"));
|
|
476
|
+
}
|
|
477
|
+
// Simple heuristic: the directory 2 levels deep is a module
|
|
478
|
+
const parts = filePath.replace(/^\.\//, "").split("/");
|
|
479
|
+
if (parts.length >= 2) {
|
|
480
|
+
const moduleName = parts[parts.length - 2];
|
|
481
|
+
const existingModule = map.modules?.find((m) => m.name === moduleName);
|
|
482
|
+
if (!existingModule) {
|
|
483
|
+
map.modules = map.modules || [];
|
|
484
|
+
map.modules.push({
|
|
485
|
+
name: moduleName,
|
|
486
|
+
path: parts.slice(0, -1).join("/"),
|
|
487
|
+
key_files: [{ path: filePath, responsibility: "auto-detected" }],
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
const existingFile = existingModule.key_files?.find((f) => f.path === filePath);
|
|
492
|
+
if (!existingFile) {
|
|
493
|
+
existingModule.key_files = existingModule.key_files || [];
|
|
494
|
+
existingModule.key_files.push({ path: filePath, responsibility: "auto-detected" });
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
map.lastUpdated = new Date().toISOString();
|
|
498
|
+
(0, fs_1.writeFileSync)(MAP_FILE, JSON.stringify(map, null, 2), "utf-8");
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
catch {
|
|
502
|
+
// silent
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
};
|
|
507
|
+
};
|
|
508
|
+
exports.default = ciel;
|
|
509
|
+
//# sourceMappingURL=index.js.map
|
package/dist/cli/check.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/cli/check.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/cli/check.ts"],"names":[],"mappings":"AAsCA,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAuC9C"}
|
package/dist/cli/check.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Check command — compare local version against
|
|
2
|
+
// Check command — compare local version against NPM registry
|
|
3
|
+
// (NPM is the canonical distribution channel for Ciel v6+)
|
|
3
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
5
|
exports.runCheck = runCheck;
|
|
5
6
|
const https_1 = require("https");
|
|
6
7
|
const utils_1 = require("./utils");
|
|
7
8
|
const version_1 = require("./version");
|
|
8
|
-
const
|
|
9
|
+
const NPM_REGISTRY = "https://registry.npmjs.org/@neikyun/ciel/latest";
|
|
9
10
|
const CIEL_VERSION = (0, version_1.getVersion)();
|
|
10
11
|
function fetchUrl(url) {
|
|
11
12
|
return new Promise((resolve, reject) => {
|
|
12
|
-
(0, https_1.get)(url, (res) => {
|
|
13
|
+
(0, https_1.get)(url, { headers: { Accept: "application/json" } }, (res) => {
|
|
13
14
|
let data = "";
|
|
14
15
|
res.on("data", (chunk) => (data += chunk));
|
|
15
|
-
res.on("end", () => resolve(data
|
|
16
|
+
res.on("end", () => resolve(data));
|
|
16
17
|
}).on("error", reject);
|
|
17
18
|
});
|
|
18
19
|
}
|
|
@@ -37,9 +38,11 @@ function compareVersions(a, b) {
|
|
|
37
38
|
}
|
|
38
39
|
async function runCheck() {
|
|
39
40
|
try {
|
|
40
|
-
const
|
|
41
|
+
const raw = await fetchUrl(NPM_REGISTRY);
|
|
42
|
+
const pkg = JSON.parse(raw);
|
|
43
|
+
const remoteVersion = pkg.version;
|
|
41
44
|
if (!remoteVersion) {
|
|
42
|
-
(0, utils_1.err)("Could not fetch
|
|
45
|
+
(0, utils_1.err)("Could not fetch latest version from NPM registry.");
|
|
43
46
|
(0, utils_1.err)("Check your internet connection.");
|
|
44
47
|
process.exit(2);
|
|
45
48
|
}
|
|
@@ -50,12 +53,18 @@ async function runCheck() {
|
|
|
50
53
|
}
|
|
51
54
|
if (cmp < 0) {
|
|
52
55
|
// Remote is newer
|
|
53
|
-
console.log(` Update available: v${CIEL_VERSION} → ${remoteVersion}`);
|
|
54
|
-
console.log("
|
|
56
|
+
console.log(` Update available: v${CIEL_VERSION} → v${remoteVersion}`);
|
|
57
|
+
console.log("");
|
|
58
|
+
console.log(" If installed globally:");
|
|
59
|
+
console.log(" npm update -g @neikyun/ciel");
|
|
60
|
+
console.log(" ciel update");
|
|
61
|
+
console.log("");
|
|
62
|
+
console.log(" If installed in project:");
|
|
63
|
+
console.log(" npm update @neikyun/ciel");
|
|
55
64
|
process.exit(0);
|
|
56
65
|
}
|
|
57
66
|
// Local is newer (dev mode)
|
|
58
|
-
(0, utils_1.say)(`Ciel v${CIEL_VERSION} (ahead of
|
|
67
|
+
(0, utils_1.say)(`Ciel v${CIEL_VERSION} (ahead of npm v${remoteVersion} — dev mode)`);
|
|
59
68
|
process.exit(0);
|
|
60
69
|
}
|
|
61
70
|
catch (error) {
|
package/dist/cli/check.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/cli/check.ts"],"names":[],"mappings":";AAAA,
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/cli/check.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/index.js
CHANGED
|
@@ -26,9 +26,9 @@ USAGE:
|
|
|
26
26
|
|
|
27
27
|
COMMANDS:
|
|
28
28
|
init Install Ciel in the current project (default)
|
|
29
|
-
update Force reinstall
|
|
29
|
+
update Force reinstall (run after npm update -g @neikyun/ciel)
|
|
30
30
|
uninstall Remove all Ciel files from the project
|
|
31
|
-
check Check
|
|
31
|
+
check Check NPM for a newer version
|
|
32
32
|
|
|
33
33
|
OPTIONS:
|
|
34
34
|
-y, --yes Skip confirmation prompt (non-interactive)
|
package/dist/cli/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAmID,wBAAsB,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA0FjE"}
|
package/dist/cli/init.js
CHANGED
|
@@ -34,13 +34,15 @@ function resolveSourceDir() {
|
|
|
34
34
|
(0, path_1.join)(process.cwd(), "../.."),
|
|
35
35
|
// Dev: running from packages/ (sub-workspace)
|
|
36
36
|
(0, path_1.join)(process.cwd(), ".."),
|
|
37
|
-
// npm: bundled assets
|
|
37
|
+
// npm: bundled assets (compiled CLI: __dirname = dist/cli/)
|
|
38
|
+
(0, path_1.join)(__dirname, "..", "..", "assets"),
|
|
39
|
+
// npm: installed globally (__dirname = dist/cli/ → go up to root)
|
|
40
|
+
(0, path_1.join)(__dirname, "..", ".."),
|
|
41
|
+
// npm: one level from assets (if __dirname is assets/)
|
|
38
42
|
(0, path_1.join)(__dirname, "..", "assets"),
|
|
39
|
-
// npm:
|
|
43
|
+
// npm: npx cache deeper
|
|
40
44
|
(0, path_1.join)(__dirname, ".."),
|
|
41
|
-
// npm: one more level up (npx cache)
|
|
42
45
|
(0, path_1.join)(__dirname, "../.."),
|
|
43
|
-
// npm: two levels up (npx cache, deeper)
|
|
44
46
|
(0, path_1.join)(__dirname, "../../.."),
|
|
45
47
|
];
|
|
46
48
|
for (const dir of candidates) {
|
|
@@ -57,6 +59,8 @@ function resolveSourceDir() {
|
|
|
57
59
|
async function downloadTemplatesToTemp() {
|
|
58
60
|
const tmpDir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), "ciel-templates-"));
|
|
59
61
|
const templatePaths = [
|
|
62
|
+
// Compiled plugin JS (for local reference, no node_modules needed)
|
|
63
|
+
"platforms/opencode/.opencode/plugins/ciel.js",
|
|
60
64
|
// OpenCode agents
|
|
61
65
|
"platforms/opencode/.opencode/agents/ciel.md",
|
|
62
66
|
"platforms/opencode/.opencode/agents/ciel-researcher.md",
|