syntropic 0.9.6 → 0.9.8

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/README.md CHANGED
@@ -27,8 +27,8 @@ You get: a disciplined development pipeline, governance doc templates, and a con
27
27
  | Rule | What it prevents | Real-world save |
28
28
  |------|-----------------|-----------------|
29
29
  | **EG1: Pre-flight** | Localhost refs, broken builds reaching production | Caught 3 localhost references pre-deploy |
30
- | **EG7: Pipeline** | AI jumping to code without research or planning | Structured 123 features across Full/Lightweight/Minimum cycles |
31
- | **EG8: Test first** | Untested changes reaching production users | Every fix caught on test page 12 verified promotions |
30
+ | **EG7: Pipeline** | AI jumping to code without research or planning | Structured 210+ features across Full/Lightweight/Minimum cycles |
31
+ | **EG8: Test first** | Untested changes reaching production users | Every change verified on test page before promotion |
32
32
  | **EG10: Env hygiene** | Env var corruption (trailing newlines, wrong formats) | Prevented silent API failures across 6 providers |
33
33
  | **EG11: Prod sync** | Access control divergence between test and production | Caught lockout bug before 8+ users were affected |
34
34
  | **EG14: Doc discipline** | Lost decisions, repeated mistakes, no project memory | Backlog, issues, and ADRs updated every cycle |
@@ -46,37 +46,74 @@ You get: a disciplined development pipeline, governance doc templates, and a con
46
46
 
47
47
  **Health Check** — daily GitHub Action with auto-remediation (npm audit fix PRs).
48
48
 
49
+ ## Deep Analysis
50
+
51
+ ```bash
52
+ syntropic analyse
53
+ ```
54
+
55
+ Run a full venture analysis from your terminal. The CLI reads your codebase (stack, routes, data model, dependencies), sends a product profile to the SyntropicWorks server, and runs the analysis through **8 philosophy lenses**:
56
+
57
+ | Lens | What it evaluates |
58
+ |------|-------------------|
59
+ | Outcome Alignment | Is the business structurally coupled to customer success? |
60
+ | Simplicity | Can anything be removed without loss? |
61
+ | Growth & Bootstrapping | What's the zero-budget path to 1,000 users? |
62
+ | Assumption Testing | What's the cheapest way to disprove each key assumption? |
63
+ | Pricing Architecture | Does the tier structure move buyers from "whether" to "which"? |
64
+ | Deep Integration | If this disappeared tomorrow, what would break? |
65
+ | Capability Access | Who's excluded today, and what barriers gatekeep them? |
66
+ | Legal & Regulatory | What creates liability, and can compliance become a moat? |
67
+
68
+ **How it works:**
69
+
70
+ 1. CLI auto-detects your project (package.json, routes, schema, infrastructure)
71
+ 2. You describe your idea or venture
72
+ 3. Server runs the full analysis — screening, report generation, 8 lens deep dives, validation plan
73
+ 4. CLI shows real-time progress as each lens completes
74
+ 5. Report displays in your terminal and saves to `.syntropic/reports/`
75
+
76
+ **Everything runs server-side.** Your idea text goes up, the finished report comes back. Analysis prompts, agent logic, and orchestration never leave the server.
77
+
78
+ ```bash
79
+ syntropic analyse --idea "glamping site in the Cotswolds" # Non-interactive
80
+ syntropic analyse --focus "pricing strategy" # With a specific focus
81
+ syntropic analyse --list # Browse past analyses
82
+ syntropic analyse --dry-run # Preview product profile only
83
+ ```
84
+
85
+ Requires a free account (`syntropic login`).
86
+
49
87
  ## Track Record
50
88
 
51
- Built in production on a real product (27 AI agents, 8-lens analyser, CLI, 7 free tools) over 10 weeks:
89
+ Built in production on a real product (31 AI agents, 8-lens analyser, CLI, 8 decision tools, 92 API endpoints) over 7 months:
52
90
 
53
- - **123 features shipped**, each through a structured pipeline
54
- - **12 test→production promotions** every feature verified on test page first
55
- - **3 bug classes prevented** by specific EG rules before reaching users
91
+ - **210+ features shipped**, each through a structured pipeline
92
+ - **98% first-try success rate** across 42 tracked PRISM cycles
93
+ - **~370k tokens saved** ~14k per complex cycle, ~9k lightweight, ~4k minimum
56
94
  - **0 undetected production incidents** after methodology adoption
57
95
 
58
96
  ## Trust & Privacy
59
97
 
60
98
  **Everything is local.** Your `.claude/` directory — rules, agents, governance docs — lives in your repo. It's yours.
61
99
 
62
- **Telemetry is metadata only.** PRISM learns from anonymous structural metadata about development cycles — never from your code, file contents, or project details. This metadata is what drives methodology improvements: which cycle weights work best for which project shapes, where pipelines break down, what patterns lead to first-pass success.
100
+ **Telemetry is metadata only.** PRISM learns from anonymous structural metadata about development cycles — never from your code, file contents, or project details.
63
101
 
64
102
  **What's collected:** cycle weight, phases run, success/failure, tool used, OS, framework detected, file count bucket (1-5 / 6-20 / 21+), governance doc presence.
65
103
  **Never collected:** file contents, file names, code, diffs, commit messages, project names, identity, API keys.
66
104
 
67
- **Why it matters.** The PRISM network grows exponentially with contributors. Every anonymous report adds signal — which cycle weights work for which project shapes, where pipelines break down, what patterns lead to first-pass success. More contributors = better data = smarter rules for everyone.
105
+ **Why it matters.** Every anonymous report adds signal — which cycle weights work for which project shapes, where pipelines break down, what patterns lead to first-pass success. More contributors = better data = smarter rules for everyone.
68
106
 
69
- **Contributors get more.** When telemetry is enabled, your PRISM sync fetches live network benchmarks — real success rates, iteration counts, and pattern intelligence computed from aggregate data across all contributors. This is how the methodology caught 3 classes of production bugs before they reached users, and how 12 test-to-production promotions were verified clean. When telemetry is disabled, you still get the full EG rules and agents, but benchmarks are static baselines only — you lose the network learning that makes rules smarter over time. No account needed — your identity is a double-hashed device fingerprint ([pseudonymisation details](https://zenodo.org/records/17894441)).
107
+ **Contributors get more.** When telemetry is enabled, your PRISM sync fetches live network benchmarks — real success rates, iteration counts, and pattern intelligence computed from aggregate data across all contributors. When disabled, you still get the full EG rules and agents, but benchmarks are static baselines only. No account needed — your identity is a double-hashed device fingerprint ([pseudonymisation details](https://zenodo.org/records/17894441)).
70
108
 
71
109
  | | Community (telemetry off) | Contributor (telemetry on) |
72
110
  |---|---|---|
73
111
  | EG rules | Full | Full |
74
112
  | Pipeline agents | Full | Full |
75
113
  | Governance docs | Full | Full |
76
- | Benchmarks | Static baselines | **Live network data** (success rates, iteration counts, failure patterns) |
77
- | Pattern intelligence | Frozen snapshot | **Updated from aggregate reports** (which cycle weights work for your stack) |
78
- | Cycle recommendations | Generic defaults | **Tuned by real data** (upgrade/downgrade triggers from contributor patterns) |
79
- | Network benefit | None | **Your reports improve rules for everyone — including you** |
114
+ | Benchmarks | Static baselines | **Live network data** |
115
+ | Pattern intelligence | Frozen snapshot | **Updated from aggregate reports** |
116
+ | Cycle recommendations | Generic defaults | **Tuned by real data** |
80
117
 
81
118
  Default: on. Opt out anytime:
82
119
 
@@ -96,15 +133,28 @@ syntropic remove
96
133
 
97
134
  ```bash
98
135
  syntropic audit # Analyse git history (no install needed)
99
- syntropic init [project-name] # Install methodology + docs
136
+ syntropic init [project-name] # Install methodology + docs + post-commit hook
100
137
  syntropic add cursor windsurf # Add tools to existing project
101
138
  syntropic remove # Clean uninstall (preserves docs)
102
139
  syntropic health # Run pre-flight checks
103
140
  syntropic report # Submit anonymous cycle report
141
+ syntropic autofire status # Check if post-commit hook is installed
142
+ syntropic autofire enable|disable # Manage the post-commit hook
104
143
  syntropic telemetry status # Check telemetry setting
105
144
  syntropic analyse # Deep-dive analysis (requires login)
106
- syntropic analyse --list # Browse your past analyses
145
+ syntropic analyse --list # Browse past analyses
146
+ syntropic analyse --dry-run # Preview product profile without sending
107
147
  syntropic login # Sign in (needed for analyse only)
148
+ syntropic logout # Sign out
149
+ syntropic whoami # Show current auth status
150
+ ```
151
+
152
+ ### `syntropic init` flags
153
+
154
+ ```bash
155
+ syntropic init --no-hook # Skip installing the post-commit hook
156
+ syntropic init --no-email # Skip the optional email prompt
157
+ syntropic init --yes # Non-interactive (defaults to all tools, no email prompt)
108
158
  ```
109
159
 
110
160
  ## How It Works
package/bin/syntropic.js CHANGED
@@ -61,6 +61,7 @@ const COMMANDS = {
61
61
  health: () => require('../commands/health'),
62
62
  telemetry: () => require('../commands/telemetry'),
63
63
  report: () => require('../commands/report'),
64
+ autofire: () => require('../commands/autofire'),
64
65
  login: () => require('../commands/login'),
65
66
  logout: () => require('../commands/logout'),
66
67
  whoami: () => require('../commands/whoami'),
@@ -92,6 +93,7 @@ if (!command || args.includes('--help') || args.includes('-h')) {
92
93
  syntropic health Run a local health check
93
94
  syntropic telemetry [cmd] Manage pseudonymised PRISM telemetry (enable/disable/status)
94
95
  syntropic report [flags] Submit a PRISM cycle report
96
+ syntropic autofire [cmd] Manage post-commit hook that auto-fires report (enable/disable/status)
95
97
  syntropic --version Show version
96
98
  syntropic --help Show this help
97
99
 
@@ -107,6 +109,8 @@ if (!command || args.includes('--help') || args.includes('-h')) {
107
109
  --domain example.com Production domain
108
110
  --test-url /test Test page path
109
111
  --prod-url / Production page path
112
+ --no-hook Skip installing post-commit autofire hook
113
+ --no-email Skip the optional email prompt
110
114
  --yes Skip interactive prompts
111
115
 
112
116
  Flags (analyse):
@@ -504,13 +504,31 @@ async function runExecute(auth, profile, idea, focus, flags) {
504
504
  const bar = '─'.repeat(50);
505
505
  console.log(` ${bar} 100%\n`);
506
506
 
507
- // Display results
508
- displayReport(profile.project_name, status.sections);
507
+ // Separate standard sections from partner outputs
508
+ const standardSections = (status.sections || []).filter(s => !s.slug?.startsWith('partner_output'));
509
+ const partnerSections = (status.sections || []).filter(s => s.slug?.startsWith('partner_output'));
509
510
 
510
- // Save locally
511
- const savedPath = saveReport(profile.project_name, status.sections);
511
+ // Display results (standard sections only)
512
+ displayReport(profile.project_name, standardSections);
513
+
514
+ // Save standard report
515
+ const savedPath = saveReport(profile.project_name, standardSections);
512
516
  console.log(`\n Saved to: ${savedPath}`);
513
517
 
518
+ // Save partner outputs as separate files
519
+ for (const ps of partnerSections) {
520
+ try {
521
+ const jsonData = typeof ps.json_data === 'string' ? JSON.parse(ps.json_data) : (ps.json_data || {});
522
+ const filename = jsonData.output_filename || `${ps.type}.md`;
523
+ const downloadsDir = path.join(require('os').homedir(), 'Downloads');
524
+ const outputPath = path.join(downloadsDir, filename);
525
+ fs.writeFileSync(outputPath, ps.markdown, 'utf8');
526
+ console.log(` Partner output: ~/${path.relative(require('os').homedir(), outputPath)}`);
527
+ } catch (partnerErr) {
528
+ // Don't fail the whole flow for a partner output save error
529
+ }
530
+ }
531
+
514
532
  // Feedback prompt
515
533
  if (isInteractive) {
516
534
  await promptFeedback(auth, sessionId);
@@ -0,0 +1,125 @@
1
+ /**
2
+ * syntropic autofire [enable|disable|status]
3
+ *
4
+ * Manages the git post-commit hook that fires `syntropic report` after each commit.
5
+ * Opt-in activation signal — turns "installed" into "used".
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ const MARKER = '# syntropic-autofire';
12
+
13
+ function hookPath(targetDir) {
14
+ return path.join(targetDir, '.git', 'hooks', 'post-commit');
15
+ }
16
+
17
+ function isInstalled(targetDir) {
18
+ const p = hookPath(targetDir);
19
+ if (!fs.existsSync(p)) return false;
20
+ try {
21
+ return fs.readFileSync(p, 'utf8').includes(MARKER);
22
+ } catch {
23
+ return false;
24
+ }
25
+ }
26
+
27
+ function install(targetDir) {
28
+ const gitDir = path.join(targetDir, '.git');
29
+ if (!fs.existsSync(gitDir)) {
30
+ return { ok: false, error: 'Not a git repository. Run `git init` first.' };
31
+ }
32
+
33
+ const hooksDir = path.join(gitDir, 'hooks');
34
+ if (!fs.existsSync(hooksDir)) fs.mkdirSync(hooksDir, { recursive: true });
35
+
36
+ const p = hookPath(targetDir);
37
+ const body = `#!/bin/sh
38
+ ${MARKER}
39
+ # Auto-fires syntropic report after each commit. Non-blocking, fire-and-forget.
40
+ # Remove with: syntropic autofire disable
41
+ command -v syntropic >/dev/null 2>&1 || command -v npx >/dev/null 2>&1 || exit 0
42
+ (syntropic report >/dev/null 2>&1 || npx -y syntropic report >/dev/null 2>&1) &
43
+ exit 0
44
+ `;
45
+
46
+ if (fs.existsSync(p)) {
47
+ const existing = fs.readFileSync(p, 'utf8');
48
+ if (existing.includes(MARKER)) return { ok: true, already: true };
49
+ return { ok: false, error: 'A post-commit hook already exists. Remove it first or merge manually.' };
50
+ }
51
+
52
+ fs.writeFileSync(p, body, { encoding: 'utf8', mode: 0o755 });
53
+ return { ok: true, already: false };
54
+ }
55
+
56
+ function uninstall(targetDir) {
57
+ const p = hookPath(targetDir);
58
+ if (!fs.existsSync(p)) return { ok: true, notFound: true };
59
+ try {
60
+ const content = fs.readFileSync(p, 'utf8');
61
+ if (!content.includes(MARKER)) {
62
+ return { ok: false, error: 'Post-commit hook exists but was not installed by syntropic. Not removed.' };
63
+ }
64
+ fs.unlinkSync(p);
65
+ return { ok: true, notFound: false };
66
+ } catch (err) {
67
+ return { ok: false, error: err.message };
68
+ }
69
+ }
70
+
71
+ async function run(args) {
72
+ const sub = (args[0] || 'status').toLowerCase();
73
+ const targetDir = process.cwd();
74
+
75
+ if (sub === 'enable') {
76
+ const res = install(targetDir);
77
+ if (!res.ok) {
78
+ console.log(`\n ${res.error}\n`);
79
+ process.exit(1);
80
+ }
81
+ if (res.already) {
82
+ console.log('\n Autofire is already enabled.\n');
83
+ } else {
84
+ console.log('\n Autofire enabled. `syntropic report` will run after every commit (non-blocking).');
85
+ console.log(' Disable anytime: syntropic autofire disable\n');
86
+ }
87
+ return;
88
+ }
89
+
90
+ if (sub === 'disable') {
91
+ const res = uninstall(targetDir);
92
+ if (!res.ok) {
93
+ console.log(`\n ${res.error}\n`);
94
+ process.exit(1);
95
+ }
96
+ if (res.notFound) {
97
+ console.log('\n Autofire was not installed. Nothing to remove.\n');
98
+ } else {
99
+ console.log('\n Autofire disabled. Post-commit hook removed.');
100
+ console.log(' Re-enable anytime: syntropic autofire enable\n');
101
+ }
102
+ return;
103
+ }
104
+
105
+ // status
106
+ const installed = isInstalled(targetDir);
107
+ console.log(`
108
+ Syntropic Autofire
109
+ ──────────────────
110
+ Status: ${installed ? 'ENABLED' : 'DISABLED'}
111
+ Path: .git/hooks/post-commit
112
+
113
+ What it does:
114
+ Runs \`syntropic report\` after every git commit — a signal to the PRISM
115
+ network that a development cycle completed. Non-blocking, fire-and-forget.
116
+ Respects \`syntropic telemetry disable\`.
117
+
118
+ Commands:
119
+ syntropic autofire enable Install the post-commit hook in this repo
120
+ syntropic autofire disable Remove the hook
121
+ syntropic autofire status Show current state
122
+ `);
123
+ }
124
+
125
+ module.exports = run;
package/commands/init.js CHANGED
@@ -14,7 +14,138 @@
14
14
  const fs = require('fs');
15
15
  const path = require('path');
16
16
  const readline = require('readline');
17
- const { ensureConfig } = require('./config-utils');
17
+ const https = require('https');
18
+ const crypto = require('crypto');
19
+ const { ensureConfig, loadConfig, clientHash } = require('./config-utils');
20
+
21
+ const API_BASE = 'https://www.syntropicworks.com';
22
+
23
+ function postJson(urlPath, body, timeoutMs = 3000) {
24
+ return new Promise((resolve) => {
25
+ try {
26
+ const url = new URL(urlPath, API_BASE);
27
+ const data = JSON.stringify(body);
28
+ const req = https.request({
29
+ hostname: url.hostname,
30
+ port: 443,
31
+ path: url.pathname,
32
+ method: 'POST',
33
+ headers: {
34
+ 'Content-Type': 'application/json',
35
+ 'Content-Length': Buffer.byteLength(data),
36
+ },
37
+ timeout: timeoutMs,
38
+ }, (res) => {
39
+ res.resume();
40
+ res.on('end', () => resolve(res.statusCode >= 200 && res.statusCode < 300));
41
+ });
42
+ req.on('error', () => resolve(false));
43
+ req.on('timeout', () => { req.destroy(); resolve(false); });
44
+ req.write(data);
45
+ req.end();
46
+ } catch {
47
+ resolve(false);
48
+ }
49
+ });
50
+ }
51
+
52
+ function detectToolAndOs() {
53
+ let tool = 'unknown';
54
+ if (process.env.CURSOR_SESSION_ID || process.env.CURSOR_TRACE_ID) tool = 'cursor';
55
+ else if (process.env.WINDSURF_SESSION) tool = 'windsurf';
56
+ else if (process.env.GITHUB_COPILOT) tool = 'copilot';
57
+ else if (process.env.CODEX_SESSION) tool = 'codex';
58
+ else if (process.env.CLAUDECODE || process.env.CLAUDE_CODE_SESSION) tool = 'claude_code';
59
+ return { tool, os: require('os').platform() };
60
+ }
61
+
62
+ function detectFramework(dir) {
63
+ const has = (f) => fs.existsSync(path.join(dir, f));
64
+ if (has('next.config.js') || has('next.config.mjs') || has('next.config.ts')) return 'nextjs';
65
+ if (has('nuxt.config.js') || has('nuxt.config.ts')) return 'nuxt';
66
+ if (has('angular.json')) return 'angular';
67
+ if (has('svelte.config.js')) return 'svelte';
68
+ if (has('vite.config.js') || has('vite.config.ts')) return 'vite';
69
+ if (has('requirements.txt') || has('pyproject.toml')) return 'python';
70
+ if (has('Cargo.toml')) return 'rust';
71
+ if (has('go.mod')) return 'go';
72
+ return 'unknown';
73
+ }
74
+
75
+ function repoHashFor(dir) {
76
+ try {
77
+ const { execSync } = require('child_process');
78
+ let identity;
79
+ try {
80
+ identity = execSync('git remote get-url origin 2>/dev/null', { cwd: dir, encoding: 'utf8' }).trim();
81
+ } catch {
82
+ identity = path.basename(dir);
83
+ }
84
+ return crypto.createHash('sha256').update(identity || path.basename(dir)).digest('hex').slice(0, 16);
85
+ } catch {
86
+ return null;
87
+ }
88
+ }
89
+
90
+ async function sendActivationPing(targetDir, selectedTools, hasHook) {
91
+ const config = loadConfig();
92
+ if (!config.telemetry) return;
93
+ const env = detectToolAndOs();
94
+ await postJson('/api/v1/prism/report', {
95
+ type: 'activation',
96
+ anon_id: clientHash(config.device_id),
97
+ cli_version: require('../package.json').version,
98
+ tool: env.tool,
99
+ os: env.os,
100
+ framework: detectFramework(targetDir),
101
+ repo_hash: repoHashFor(targetDir),
102
+ tools: selectedTools,
103
+ has_hook: !!hasHook,
104
+ source: 'init',
105
+ });
106
+ }
107
+
108
+ async function sendWaitlistEmail(email, targetDir) {
109
+ const config = loadConfig();
110
+ const env = detectToolAndOs();
111
+ await postJson('/api/v1/cli/waitlist', {
112
+ email,
113
+ anon_id: config.telemetry ? clientHash(config.device_id) : null,
114
+ cli_version: require('../package.json').version,
115
+ tool: env.tool,
116
+ os: env.os,
117
+ framework: detectFramework(targetDir),
118
+ source: 'init',
119
+ }, 5000);
120
+ }
121
+
122
+ function installPostCommitHook(targetDir) {
123
+ const gitDir = path.join(targetDir, '.git');
124
+ if (!fs.existsSync(gitDir)) return { installed: false, reason: 'not a git repo' };
125
+
126
+ const hooksDir = path.join(gitDir, 'hooks');
127
+ if (!fs.existsSync(hooksDir)) fs.mkdirSync(hooksDir, { recursive: true });
128
+
129
+ const hookPath = path.join(hooksDir, 'post-commit');
130
+ const marker = '# syntropic-autofire';
131
+ const body = `#!/bin/sh
132
+ ${marker}
133
+ # Auto-fires syntropic report after each commit. Non-blocking, fire-and-forget.
134
+ # Remove with: syntropic autofire disable
135
+ command -v syntropic >/dev/null 2>&1 || command -v npx >/dev/null 2>&1 || exit 0
136
+ (syntropic report >/dev/null 2>&1 || npx -y syntropic report >/dev/null 2>&1) &
137
+ exit 0
138
+ `;
139
+
140
+ if (fs.existsSync(hookPath)) {
141
+ const existing = fs.readFileSync(hookPath, 'utf8');
142
+ if (existing.includes(marker)) return { installed: true, reason: 'already installed' };
143
+ return { installed: false, reason: 'post-commit hook exists (not overwritten)' };
144
+ }
145
+
146
+ fs.writeFileSync(hookPath, body, { encoding: 'utf8', mode: 0o755 });
147
+ return { installed: true, reason: 'new hook' };
148
+ }
18
149
 
19
150
  const TEMPLATES_DIR = path.join(__dirname, '..', 'templates');
20
151
 
@@ -316,6 +447,39 @@ async function run(args) {
316
447
  }
317
448
  }
318
449
 
450
+ // Install post-commit hook (auto-fires `syntropic report` after every commit)
451
+ // Skipped with --no-hook flag or when not a git repo
452
+ let hookResult = { installed: false, reason: 'skipped' };
453
+ if (!flags['no-hook']) {
454
+ hookResult = installPostCommitHook(targetDir);
455
+ if (hookResult.installed) {
456
+ console.log(' hook .git/hooks/post-commit (auto-fires `syntropic report` after each commit)');
457
+ } else if (hookResult.reason === 'not a git repo') {
458
+ console.log(' skip post-commit hook (not a git repo yet — run `git init` then `syntropic autofire enable`)');
459
+ } else if (hookResult.reason !== 'already installed') {
460
+ console.log(` skip post-commit hook (${hookResult.reason})`);
461
+ }
462
+ }
463
+
464
+ // Optional email capture — helps measure real users vs npm install noise
465
+ // Only prompts when interactive and user hasn't passed --no-email
466
+ if (isInteractive && !flags['no-email']) {
467
+ console.log('');
468
+ const email = await ask(' Optional: email for roadmap updates + early access to paid features (Enter to skip): ');
469
+ if (email && email.includes('@') && email.length < 254) {
470
+ try {
471
+ await sendWaitlistEmail(email.trim(), targetDir);
472
+ console.log(' Thanks. No spam — just occasional updates.');
473
+ } catch { /* fire-and-forget */ }
474
+ }
475
+ }
476
+
477
+ // Fire activation ping — measures real `init` completion, not just npm pulls
478
+ // Respects telemetry opt-out
479
+ try {
480
+ await sendActivationPing(targetDir, selectedTools, hookResult.installed);
481
+ } catch { /* fire-and-forget, never block init */ }
482
+
319
483
  console.log(`
320
484
  Done! Your project is set up with the Syntropic pipeline.
321
485
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "syntropic",
3
- "version": "0.9.6",
3
+ "version": "0.9.8",
4
4
  "description": "Ship better software with a proven development methodology. Audit your git history, install disciplined rules, and track iterations — for Claude Code, Cursor, Windsurf, GitHub Copilot, and OpenAI Codex.",
5
5
  "bin": {
6
6
  "syntropic": "./bin/syntropic.js"