@neikyun/ciel 5.1.6 → 5.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neikyun/ciel",
3
- "version": "5.1.6",
3
+ "version": "5.1.7",
4
4
  "description": "Ciel — Deep-reasoning pipeline for LLM-assisted development. OpenCode plugin + multi-platform CLI (OpenCode, Claude Code, more).",
5
5
  "main": "./dist/plugin/index.js",
6
6
  "types": "./dist/plugin/index.d.ts",
@@ -11,12 +11,14 @@
11
11
  "dist",
12
12
  "assets",
13
13
  "bin",
14
+ "scripts/postinstall.cjs",
14
15
  "README.md"
15
16
  ],
16
17
  "scripts": {
17
18
  "build": "node scripts/copy-assets.cjs && tsc",
18
19
  "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true});require('fs').rmSync('assets',{recursive:true,force:true})\"",
19
20
  "test": "npx tsx --test test/**/*.test.ts",
21
+ "postinstall": "node scripts/postinstall.cjs",
20
22
  "prepublishOnly": "npm run build && npm test"
21
23
  },
22
24
  "dependencies": {
@@ -0,0 +1,231 @@
1
+ #!/usr/bin/env node
2
+ // Ciel postinstall — auto-configure après npm install
3
+ //
4
+ // S'exécute automatiquement après `npm install @neikyun/ciel`.
5
+ // Détecte OpenCode / Claude Code, propose de configurer, et installe.
6
+ //
7
+ // Désactivable avec : CI=true npm install @neikyun/ciel
8
+ // npm install @neikyun/ciel --ignore-scripts
9
+
10
+ const { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync, chmodSync } = require("fs");
11
+ const { join, dirname } = require("path");
12
+ const { execSync } = require("child_process");
13
+
14
+ const CIEL_VERSION = "5.1.6";
15
+
16
+ // ----- Colors -----
17
+ const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
18
+ const green = (s) => `\x1b[32m${s}\x1b[0m`;
19
+ const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
20
+ const bold = (s) => `\x1b[1m${s}\x1b[0m`;
21
+
22
+ // ----- Helpers -----
23
+ function detectPlatform(targetDir) {
24
+ const platforms = [];
25
+ if (existsSync(join(targetDir, "opencode.json")) || existsSync(join(targetDir, ".opencode"))) {
26
+ platforms.push("opencode");
27
+ }
28
+ if (existsSync(join(targetDir, ".claude/settings.json")) || existsSync(join(targetDir, ".claude"))) {
29
+ platforms.push("claude");
30
+ }
31
+ return platforms;
32
+ }
33
+
34
+ function resolveAssetsDir() {
35
+ const candidates = [
36
+ // npm install local (workspace)
37
+ join(__dirname, "..", "assets"),
38
+ // npm install global
39
+ join(__dirname, "..", "..", "assets"),
40
+ // GitHub fallback (via npx ciel)
41
+ null,
42
+ ];
43
+ for (const dir of candidates) {
44
+ if (dir && existsSync(join(dir, "platforms/opencode/.opencode/agents/ciel.md"))) {
45
+ return dir;
46
+ }
47
+ }
48
+ return null;
49
+ }
50
+
51
+ function copyDir(src, dest) {
52
+ if (!existsSync(src)) return 0;
53
+ const { readdirSync } = require("fs");
54
+ let count = 0;
55
+ mkdirSync(dest, { recursive: true });
56
+ for (const entry of readdirSync(src, { withFileTypes: true })) {
57
+ const s = join(src, entry.name);
58
+ const d = join(dest, entry.name);
59
+ if (entry.isDirectory()) {
60
+ count += copyDir(s, d);
61
+ } else {
62
+ copyFileSync(s, d);
63
+ if (s.endsWith(".sh")) try { chmodSync(d, 0o755); } catch {}
64
+ count++;
65
+ }
66
+ }
67
+ return count;
68
+ }
69
+
70
+ function patchOpencodeJson(targetDir) {
71
+ const configPath = join(targetDir, "opencode.json");
72
+ if (!existsSync(configPath)) return false;
73
+ try {
74
+ const raw = readFileSync(configPath, "utf-8");
75
+ const config = JSON.parse(raw);
76
+ if (!config.plugin) config.plugin = [];
77
+ if (!config.plugin.includes("@neikyun/ciel")) {
78
+ config.plugin.push("@neikyun/ciel");
79
+ }
80
+ if (!config.instructions) config.instructions = [];
81
+ if (!config.instructions.includes("AGENTS.md")) {
82
+ config.instructions.push("AGENTS.md");
83
+ }
84
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
85
+ return true;
86
+ } catch {
87
+ return false;
88
+ }
89
+ }
90
+
91
+ function installForPlatform(targetDir, assetsDir, platform) {
92
+ let count = 0;
93
+ if (platform === "opencode") {
94
+ // Copy agents
95
+ const agentsSrc = join(assetsDir, "platforms/opencode/.opencode/agents");
96
+ const agentsDest = join(targetDir, ".opencode/agents");
97
+ count += copyDir(agentsSrc, agentsDest);
98
+
99
+ // Copy commands
100
+ const cmdsSrc = join(assetsDir, "platforms/opencode/.opencode/commands");
101
+ const cmdsDest = join(targetDir, ".opencode/commands");
102
+ count += copyDir(cmdsSrc, cmdsDest);
103
+
104
+ // Copy AGENTS.md
105
+ const agentsMdSrc = join(assetsDir, "platforms/opencode/AGENTS.md");
106
+ if (existsSync(agentsMdSrc)) {
107
+ copyFileSync(agentsMdSrc, join(targetDir, "AGENTS.md"));
108
+ count++;
109
+ }
110
+
111
+ // Patch opencode.json
112
+ if (patchOpencodeJson(targetDir)) count++;
113
+
114
+ return count;
115
+ }
116
+
117
+ if (platform === "claude") {
118
+ // Copy agents
119
+ const agentsSrc = join(assetsDir, ".claude/agents");
120
+ const agentsDest = join(targetDir, ".claude/agents");
121
+ count += copyDir(agentsSrc, agentsDest);
122
+
123
+ // Copy hooks
124
+ const hooksSrc = join(assetsDir, ".claude/hooks");
125
+ const hooksDest = join(targetDir, ".claude/hooks");
126
+ count += copyDir(hooksSrc, hooksDest);
127
+
128
+ // Copy settings
129
+ const settingsSrc = join(assetsDir, ".claude/settings.json");
130
+ if (existsSync(settingsSrc)) {
131
+ copyFileSync(settingsSrc, join(targetDir, ".claude/settings.json"));
132
+ count++;
133
+ }
134
+
135
+ // Copy CLAUDE.md
136
+ const claudeMdSrc = join(assetsDir, "CLAUDE.md");
137
+ if (existsSync(claudeMdSrc)) {
138
+ copyFileSync(claudeMdSrc, join(targetDir, "CLAUDE.md"));
139
+ count++;
140
+ }
141
+
142
+ return count;
143
+ }
144
+
145
+ return 0;
146
+ }
147
+
148
+ // ===== MAIN =====
149
+ async function main() {
150
+ // Skip if CI env or --ignore-scripts was used
151
+ if (process.env.CI || process.env.NO_CIEL_POSTINSTALL) {
152
+ return;
153
+ }
154
+
155
+ const targetDir = process.cwd();
156
+ const platforms = detectPlatform(targetDir);
157
+
158
+ if (platforms.length === 0) {
159
+ // No recognized platform — silent exit (tool installed in non-Ciel project)
160
+ return;
161
+ }
162
+
163
+ const assetsDir = resolveAssetsDir();
164
+ if (!assetsDir) {
165
+ // Assets not bundled — postinstall can't configure
166
+ return;
167
+ }
168
+
169
+ // Check if already configured
170
+ const alreadyConfigured = existsSync(join(targetDir, ".ciel/map.json"));
171
+
172
+ if (alreadyConfigured) {
173
+ // Already set up, skip prompt
174
+ return;
175
+ }
176
+
177
+ // ----- Interactive prompt -----
178
+ const platformNames = platforms.join(" + ");
179
+ console.log(`\n ${bold("✦ Ciel v" + CIEL_VERSION)} — detecté pour ${cyan(platformNames)}`);
180
+
181
+ // Try to read from stdin for the prompt
182
+ let answer = "y";
183
+ try {
184
+ const readline = require("readline");
185
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
186
+ answer = await new Promise((resolve) => {
187
+ rl.question(` ${yellow("?")} Configurer Ciel dans ce projet ? ${green("(Y/n)")} `, (a) => {
188
+ rl.close();
189
+ resolve(a.trim().toLowerCase() || "y");
190
+ });
191
+ });
192
+ } catch {
193
+ // Non-interactive, use default
194
+ }
195
+
196
+ if (answer !== "y" && answer !== "yes") {
197
+ console.log(` ${cyan("→")} Annulé. Lancez ${green("npx ciel init")} plus tard.\n`);
198
+ return;
199
+ }
200
+
201
+ // ----- Install -----
202
+ // Create .ciel/ state dir
203
+ mkdirSync(join(targetDir, ".ciel"), { recursive: true });
204
+ if (!existsSync(join(targetDir, ".ciel/map.json"))) {
205
+ writeFileSync(join(targetDir, ".ciel/map.json"), JSON.stringify({ modules: [], lastUpdated: new Date().toISOString() }), "utf-8");
206
+ }
207
+ if (!existsSync(join(targetDir, ".ciel/memory.json"))) {
208
+ writeFileSync(join(targetDir, ".ciel/memory.json"), "{}", "utf-8");
209
+ }
210
+ if (!existsSync(join(targetDir, ".ciel/parking.md"))) {
211
+ writeFileSync(join(targetDir, ".ciel/parking.md"), "# Ciel Parking Lot -- Decouvertes fortuites\n\n", "utf-8");
212
+ }
213
+
214
+ let total = 0;
215
+ for (const p of platforms) {
216
+ const n = installForPlatform(targetDir, assetsDir, p);
217
+ total += n;
218
+ console.log(` ${green("✓")} ${p}: ${n} fichiers installés`);
219
+ }
220
+
221
+ console.log(`\n ${green("✓")} Ciel v${CIEL_VERSION} prêt !`);
222
+ for (const p of platforms) {
223
+ if (p === "opencode") console.log(` → OpenCode: plugin chargé automatiquement`);
224
+ if (p === "claude") console.log(` → Claude Code: redémarrez ${cyan("claude .")}`);
225
+ }
226
+ console.log(` → CLI: ${green("npx ciel")} check | init | update | uninstall\n`);
227
+ }
228
+
229
+ main().catch(() => {
230
+ // Silent fail — postinstall ne doit jamais bloquer npm install
231
+ });