instar 1.0.13 → 1.1.0
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 +1 -1
- package/dist/cli.js +52 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.d.ts +17 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +156 -64
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +68 -4
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/setup.d.ts +3 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +67 -28
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/worktree.d.ts +17 -4
- package/dist/commands/worktree.d.ts.map +1 -1
- package/dist/commands/worktree.js +28 -4
- package/dist/commands/worktree.js.map +1 -1
- package/dist/core/AgentWorktreeDetector.d.ts +63 -0
- package/dist/core/AgentWorktreeDetector.d.ts.map +1 -0
- package/dist/core/AgentWorktreeDetector.js +329 -0
- package/dist/core/AgentWorktreeDetector.js.map +1 -0
- package/dist/core/InstarWorktreeManager.d.ts +99 -0
- package/dist/core/InstarWorktreeManager.d.ts.map +1 -0
- package/dist/core/InstarWorktreeManager.js +447 -0
- package/dist/core/InstarWorktreeManager.js.map +1 -0
- package/dist/core/MachineIdentity.d.ts.map +1 -1
- package/dist/core/MachineIdentity.js +2 -0
- package/dist/core/MachineIdentity.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +25 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +258 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/monitoring/PromptGate.d.ts +12 -0
- package/dist/monitoring/PromptGate.d.ts.map +1 -1
- package/dist/monitoring/PromptGate.js +44 -0
- package/dist/monitoring/PromptGate.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +17 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/package.json +1 -1
- package/scripts/lint-no-direct-destructive.js +7 -0
- package/scripts/resolve-release-tier.mjs +192 -0
- package/src/data/builtin-manifest.json +56 -48
- package/src/scaffold/templates.ts +17 -0
- package/src/templates/scripts/instar-watchdog.sh +169 -6
- package/src/templates/scripts/instar-worktree-create.sh +79 -0
- package/upgrades/1.1.0.md +320 -0
- package/upgrades/side-effects/agent-worktree-convention-layer-1-2-5.md +280 -0
- package/upgrades/side-effects/agent-worktree-convention-layer-3-4.md +285 -0
- package/upgrades/side-effects/feat-fix-parent-framework-intercept.md +59 -0
- package/upgrades/side-effects/feat-install-claude-gating.md +67 -0
- package/upgrades/side-effects/feat-install-framework-flag.md +65 -0
- package/upgrades/side-effects/feat-portability-shadow-capabilities.md +63 -0
- package/upgrades/side-effects/feat-setup-framework-flag.md +65 -0
- package/upgrades/side-effects/lockdown-layer-2.md +123 -0
- package/upgrades/side-effects/watchdog-bind-failure-probe.md +174 -0
package/dist/commands/init.js
CHANGED
|
@@ -67,6 +67,25 @@ function findFreePortFallback() {
|
|
|
67
67
|
}
|
|
68
68
|
return 4040; // All ports in range are busy — return default and let server fail with a clear error
|
|
69
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Resolve the user-facing `--framework` choice into the persisted
|
|
72
|
+
* `enabledFrameworks` array stored in `.instar/config.json`. The migrator,
|
|
73
|
+
* sentinel, and runtime spawn paths all read this single field. Default
|
|
74
|
+
* (no flag) is `['claude-code']` so behavior is unchanged for existing
|
|
75
|
+
* users and the historical `npx instar` flow.
|
|
76
|
+
*/
|
|
77
|
+
export function resolveEnabledFrameworks(choice) {
|
|
78
|
+
switch (choice) {
|
|
79
|
+
case 'codex-cli':
|
|
80
|
+
return ['codex-cli'];
|
|
81
|
+
case 'both':
|
|
82
|
+
return ['claude-code', 'codex-cli'];
|
|
83
|
+
case 'claude-code':
|
|
84
|
+
case undefined:
|
|
85
|
+
default:
|
|
86
|
+
return ['claude-code'];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
70
89
|
/**
|
|
71
90
|
* Main init entry point. Handles both fresh and existing project modes.
|
|
72
91
|
*/
|
|
@@ -101,6 +120,11 @@ async function initFreshProject(projectName, options) {
|
|
|
101
120
|
console.log(` Project names must start with a letter or number and contain only letters, numbers, dots, hyphens, and underscores.`);
|
|
102
121
|
process.exit(1);
|
|
103
122
|
}
|
|
123
|
+
// Resolve framework choice once for the whole init flow. Default unchanged
|
|
124
|
+
// for users who don't pass --framework. PR 2 of 4 (portability install
|
|
125
|
+
// upgrade): codex-only and gemini-only installs skip all `.claude/` writes.
|
|
126
|
+
const enabledFrameworks = resolveEnabledFrameworks(options.framework);
|
|
127
|
+
const claudeEnabled = enabledFrameworks.includes('claude-code');
|
|
104
128
|
if (projectName === '.' || projectName === '..' || projectName.includes('/') || projectName.includes('\\')) {
|
|
105
129
|
console.log(pc.red(` Invalid project name: "${projectName}"`));
|
|
106
130
|
process.exit(1);
|
|
@@ -261,6 +285,7 @@ async function initFreshProject(projectName, options) {
|
|
|
261
285
|
visibility: 'public',
|
|
262
286
|
capabilities: ['chat'],
|
|
263
287
|
},
|
|
288
|
+
enabledFrameworks: [...resolveEnabledFrameworks(options.framework)],
|
|
264
289
|
};
|
|
265
290
|
const configFilePath = path.join(stateDir, 'config.json');
|
|
266
291
|
fs.writeFileSync(configFilePath, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
@@ -291,22 +316,30 @@ async function initFreshProject(projectName, options) {
|
|
|
291
316
|
const canonicalState = new CanonicalState({ stateDir });
|
|
292
317
|
canonicalState.initialize(projectName, projectDir);
|
|
293
318
|
console.log(` ${pc.green('✓')} Created .instar/quick-facts.json, anti-patterns.json, project-registry.json`);
|
|
294
|
-
// Create .claude/ structure
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
319
|
+
// Create .claude/ structure (gated on claude-code enabled — PR 2 of 4)
|
|
320
|
+
if (claudeEnabled) {
|
|
321
|
+
installClaudeSettings(projectDir, port);
|
|
322
|
+
console.log(` ${pc.green('✓')} Created .claude/settings.json`);
|
|
323
|
+
installHealthWatchdog(projectDir, port, projectName);
|
|
324
|
+
console.log(` ${pc.green('✓')} Created .claude/scripts/health-watchdog.sh`);
|
|
325
|
+
installSmartFetch(projectDir);
|
|
326
|
+
console.log(` ${pc.green('✓')} Created .claude/scripts/smart-fetch.py (agentic web conventions)`);
|
|
327
|
+
installGitSyncGate(projectDir);
|
|
328
|
+
console.log(` ${pc.green('✓')} Created .claude/scripts/git-sync-gate.sh (git sync pre-screening)`);
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
console.log(` ${pc.dim('·')} Skipped .claude/ scaffolding (enabledFrameworks: ${enabledFrameworks.join(', ')})`);
|
|
332
|
+
}
|
|
333
|
+
// Framework-neutral: always install. Lives in .instar/scripts/.
|
|
303
334
|
installSerendipityCapture(projectDir);
|
|
304
335
|
console.log(` ${pc.green('✓')} Created .instar/scripts/serendipity-capture.sh`);
|
|
305
|
-
// Create .claude/skills/ directory and install built-in skills
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
336
|
+
// Create .claude/skills/ directory and install built-in skills (gated)
|
|
337
|
+
if (claudeEnabled) {
|
|
338
|
+
const skillsDir = path.join(projectDir, '.claude', 'skills');
|
|
339
|
+
fs.mkdirSync(skillsDir, { recursive: true });
|
|
340
|
+
installBuiltinSkills(skillsDir, port);
|
|
341
|
+
console.log(` ${pc.green('✓')} Created .claude/skills/ (with built-in evolution skills)`);
|
|
342
|
+
}
|
|
310
343
|
// Phase 2 — install built-in agentmd jobs (markdown templates + manifests).
|
|
311
344
|
// Legacy jobs.json continues to seed below; the loader handles overlap.
|
|
312
345
|
try {
|
|
@@ -319,10 +352,15 @@ async function initFreshProject(projectName, options) {
|
|
|
319
352
|
catch (err) {
|
|
320
353
|
console.log(` ${pc.yellow('!')} Built-in agentmd jobs install skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
321
354
|
}
|
|
322
|
-
// Write CLAUDE.md (standalone version for fresh projects)
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
355
|
+
// Write CLAUDE.md (standalone version for fresh projects) — gated on
|
|
356
|
+
// claude-code enabled. For codex-only/gemini-only installs the rich
|
|
357
|
+
// Claude capability doc is meaningless and is skipped; AGENTS.md /
|
|
358
|
+
// GEMINI.md are rendered from canonical AGENT.md below.
|
|
359
|
+
if (claudeEnabled) {
|
|
360
|
+
const claudeMd = generateClaudeMd(projectName, identity.name, port, false);
|
|
361
|
+
fs.writeFileSync(path.join(projectDir, 'CLAUDE.md'), claudeMd);
|
|
362
|
+
console.log(` ${pc.green('✓')} Created CLAUDE.md`);
|
|
363
|
+
}
|
|
326
364
|
// Additively render non-Claude identity shadows (AGENTS.md / GEMINI.md)
|
|
327
365
|
// from canonical .instar/AGENT.md so a Codex/Gemini install has its
|
|
328
366
|
// identity file from init-time, not only after the first server spawn.
|
|
@@ -445,6 +483,10 @@ node_modules/
|
|
|
445
483
|
async function initExistingProject(options) {
|
|
446
484
|
const projectDir = path.resolve(options.dir || process.cwd());
|
|
447
485
|
const projectName = options.name || path.basename(projectDir);
|
|
486
|
+
// Framework choice — gates `.claude/` writes for codex-only installs.
|
|
487
|
+
// Default behavior unchanged for users who don't pass --framework.
|
|
488
|
+
const enabledFrameworks = resolveEnabledFrameworks(options.framework);
|
|
489
|
+
const claudeEnabled = enabledFrameworks.includes('claude-code');
|
|
448
490
|
// Auto-allocate a port if not explicitly specified (multi-instance support)
|
|
449
491
|
let port;
|
|
450
492
|
if (options.port) {
|
|
@@ -559,6 +601,7 @@ async function initExistingProject(options) {
|
|
|
559
601
|
visibility: 'public',
|
|
560
602
|
capabilities: ['chat'],
|
|
561
603
|
},
|
|
604
|
+
enabledFrameworks: [...resolveEnabledFrameworks(options.framework)],
|
|
562
605
|
};
|
|
563
606
|
const configPath = path.join(stateDir, 'config.json');
|
|
564
607
|
if (!fs.existsSync(configPath)) {
|
|
@@ -636,26 +679,30 @@ async function initExistingProject(options) {
|
|
|
636
679
|
if (stateResult.created.length > 0) {
|
|
637
680
|
console.log(pc.green(' Created:') + ` canonical state (${stateResult.created.join(', ')})`);
|
|
638
681
|
}
|
|
639
|
-
// Configure Claude Code settings with hooks
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
682
|
+
// Configure Claude Code settings with hooks (gated — PR 2 of 4)
|
|
683
|
+
if (claudeEnabled) {
|
|
684
|
+
installClaudeSettings(projectDir, port);
|
|
685
|
+
console.log(pc.green(' Created:') + ' .claude/settings.json (hook configuration)');
|
|
686
|
+
installHealthWatchdog(projectDir, port, projectName);
|
|
687
|
+
console.log(pc.green(' Created:') + ' .claude/scripts/health-watchdog.sh');
|
|
688
|
+
installSmartFetch(projectDir);
|
|
689
|
+
console.log(pc.green(' Created:') + ' .claude/scripts/smart-fetch.py (agentic web conventions)');
|
|
690
|
+
installGitSyncGate(projectDir);
|
|
691
|
+
console.log(pc.green(' Created:') + ' .claude/scripts/git-sync-gate.sh (git sync pre-screening)');
|
|
692
|
+
}
|
|
693
|
+
else {
|
|
694
|
+
console.log(pc.dim(' Skipped:') + ` .claude/ scaffolding (enabledFrameworks: ${enabledFrameworks.join(', ')})`);
|
|
695
|
+
}
|
|
696
|
+
// Framework-neutral: always install. Lives in .instar/scripts/.
|
|
652
697
|
installSerendipityCapture(projectDir);
|
|
653
698
|
console.log(pc.green(' Created:') + ' .instar/scripts/serendipity-capture.sh');
|
|
654
|
-
// Create .claude/skills/ directory and install built-in skills
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
699
|
+
// Create .claude/skills/ directory and install built-in skills (gated)
|
|
700
|
+
if (claudeEnabled) {
|
|
701
|
+
const skillsDir = path.join(projectDir, '.claude', 'skills');
|
|
702
|
+
fs.mkdirSync(skillsDir, { recursive: true });
|
|
703
|
+
installBuiltinSkills(skillsDir, port);
|
|
704
|
+
console.log(pc.green(' Created:') + ' .claude/skills/ (with built-in evolution skills)');
|
|
705
|
+
}
|
|
659
706
|
// Append to .gitignore
|
|
660
707
|
const gitignorePath = path.join(projectDir, '.gitignore');
|
|
661
708
|
const instarIgnores = '\n# Instar per-machine state (sessions, logs, secrets)\n.instar/state/\n.instar/logs/\n.instar/config.json\n';
|
|
@@ -748,6 +795,9 @@ async function initStandaloneAgent(agentName, options) {
|
|
|
748
795
|
console.log(' Maximum 64 characters.');
|
|
749
796
|
process.exit(1);
|
|
750
797
|
}
|
|
798
|
+
// Framework choice — gates `.claude/` writes. Default unchanged.
|
|
799
|
+
const enabledFrameworks = resolveEnabledFrameworks(options.framework);
|
|
800
|
+
const claudeEnabled = enabledFrameworks.includes('claude-code');
|
|
751
801
|
const projectDir = path.join(standaloneAgentsDir(), agentName);
|
|
752
802
|
const stateDir = path.join(projectDir, '.instar');
|
|
753
803
|
// Check if already exists
|
|
@@ -854,31 +904,40 @@ async function initStandaloneAgent(agentName, options) {
|
|
|
854
904
|
enabled: true,
|
|
855
905
|
type: 'quick',
|
|
856
906
|
},
|
|
907
|
+
enabledFrameworks: [...resolveEnabledFrameworks(options.framework)],
|
|
857
908
|
};
|
|
858
909
|
fs.writeFileSync(path.join(stateDir, 'config.json'), JSON.stringify(config, null, 2));
|
|
859
910
|
console.log(` ${pc.green('✓')} Created config.json`);
|
|
860
911
|
// Write empty jobs and users
|
|
861
912
|
fs.writeFileSync(path.join(stateDir, 'jobs.json'), JSON.stringify([], null, 2));
|
|
862
913
|
fs.writeFileSync(path.join(stateDir, 'users.json'), JSON.stringify([], null, 2));
|
|
863
|
-
// Create CLAUDE.md at project root
|
|
864
|
-
|
|
865
|
-
|
|
914
|
+
// Create CLAUDE.md at project root (gated on claude-code enabled —
|
|
915
|
+
// PR 2 of 4 of the install/wizard portability series).
|
|
916
|
+
if (claudeEnabled) {
|
|
917
|
+
fs.writeFileSync(path.join(projectDir, 'CLAUDE.md'), generateClaudeMd(agentName, agentName, port, false));
|
|
918
|
+
console.log(` ${pc.green('✓')} Created CLAUDE.md`);
|
|
919
|
+
}
|
|
866
920
|
// Additively render non-Claude identity shadows (AGENTS.md / GEMINI.md)
|
|
867
921
|
// from canonical .instar/AGENT.md — see the paired call in the primary
|
|
868
922
|
// init path. claude-code is intentionally skipped.
|
|
869
923
|
for (const s of renderNonClaudeIdentityShadows(projectDir, { stateDir })) {
|
|
870
924
|
console.log(` ${pc.green('✓')} Created ${path.relative(projectDir, s)}`);
|
|
871
925
|
}
|
|
872
|
-
// Create .claude/ structure
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
926
|
+
// Create .claude/ structure (gated)
|
|
927
|
+
if (claudeEnabled) {
|
|
928
|
+
const claudeDir = path.join(projectDir, '.claude');
|
|
929
|
+
fs.mkdirSync(path.join(claudeDir, 'scripts'), { recursive: true });
|
|
930
|
+
fs.mkdirSync(path.join(claudeDir, 'skills'), { recursive: true });
|
|
931
|
+
fs.writeFileSync(path.join(claudeDir, 'settings.json'), JSON.stringify({
|
|
932
|
+
hooks: {
|
|
933
|
+
PreToolUse: [],
|
|
934
|
+
PostToolUse: [],
|
|
935
|
+
},
|
|
936
|
+
}, null, 2));
|
|
937
|
+
}
|
|
938
|
+
else {
|
|
939
|
+
console.log(` ${pc.dim('·')} Skipped .claude/ scaffolding (enabledFrameworks: ${enabledFrameworks.join(', ')})`);
|
|
940
|
+
}
|
|
882
941
|
// Create .gitignore
|
|
883
942
|
fs.writeFileSync(path.join(projectDir, '.gitignore'), [
|
|
884
943
|
'# Runtime state',
|
|
@@ -3170,25 +3229,39 @@ If everything is coherent and no reflection is needed, exit silently. Only repor
|
|
|
3170
3229
|
*/
|
|
3171
3230
|
export function refreshHooksAndSettings(projectDir, stateDir) {
|
|
3172
3231
|
installHooks(stateDir);
|
|
3173
|
-
// Read port from config.json so HTTP hooks get
|
|
3232
|
+
// Read port + enabledFrameworks from config.json so HTTP hooks get
|
|
3233
|
+
// resolved URLs AND Claude-Code-only writes can be gated for codex-only
|
|
3234
|
+
// installs (portability audit PR 2 of 4).
|
|
3174
3235
|
let serverPort;
|
|
3236
|
+
let enabledFrameworks = ['claude-code'];
|
|
3175
3237
|
try {
|
|
3176
3238
|
const configPath = path.join(stateDir, 'config.json');
|
|
3177
3239
|
if (fs.existsSync(configPath)) {
|
|
3178
3240
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
3179
3241
|
serverPort = config.port;
|
|
3242
|
+
if (Array.isArray(config.enabledFrameworks) && config.enabledFrameworks.length > 0) {
|
|
3243
|
+
const filtered = config.enabledFrameworks.filter((f) => f === 'claude-code' || f === 'codex-cli');
|
|
3244
|
+
if (filtered.length > 0)
|
|
3245
|
+
enabledFrameworks = filtered;
|
|
3246
|
+
}
|
|
3180
3247
|
}
|
|
3181
3248
|
}
|
|
3182
3249
|
catch { /* non-fatal */ }
|
|
3183
|
-
|
|
3184
|
-
|
|
3250
|
+
const claudeEnabled = enabledFrameworks.includes('claude-code');
|
|
3251
|
+
if (claudeEnabled) {
|
|
3252
|
+
installClaudeSettings(projectDir, serverPort);
|
|
3253
|
+
refreshClaudeMd(projectDir, stateDir);
|
|
3254
|
+
}
|
|
3185
3255
|
refreshJobs(stateDir);
|
|
3186
3256
|
refreshScripts(projectDir, stateDir);
|
|
3187
|
-
// Deploy any missing built-in skills
|
|
3188
|
-
//
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3257
|
+
// Deploy any missing built-in skills. Skills target `.claude/skills/` so
|
|
3258
|
+
// they're Claude-Code-only by destination; skip for codex-only installs to
|
|
3259
|
+
// avoid creating a `.claude/` directory the operator did not ask for.
|
|
3260
|
+
if (claudeEnabled) {
|
|
3261
|
+
const skillsDir = path.join(projectDir, '.claude', 'skills');
|
|
3262
|
+
fs.mkdirSync(skillsDir, { recursive: true });
|
|
3263
|
+
installBuiltinSkills(skillsDir, serverPort ?? 4321);
|
|
3264
|
+
}
|
|
3192
3265
|
}
|
|
3193
3266
|
/**
|
|
3194
3267
|
* Merge new default jobs into existing jobs.json without overwriting user changes.
|
|
@@ -3312,19 +3385,38 @@ function refreshScripts(projectDir, stateDir) {
|
|
|
3312
3385
|
if (!config)
|
|
3313
3386
|
return;
|
|
3314
3387
|
const port = config.port || 4040;
|
|
3315
|
-
//
|
|
3388
|
+
// Resolve enabled-frameworks for `.claude/scripts/` gating (PR 2 of 4).
|
|
3389
|
+
// Default ['claude-code'] preserves historical behavior.
|
|
3390
|
+
const enabled = (() => {
|
|
3391
|
+
const raw = config.enabledFrameworks;
|
|
3392
|
+
if (Array.isArray(raw) && raw.length > 0) {
|
|
3393
|
+
const filtered = raw.filter((f) => f === 'claude-code' || f === 'codex-cli');
|
|
3394
|
+
if (filtered.length > 0)
|
|
3395
|
+
return filtered;
|
|
3396
|
+
}
|
|
3397
|
+
return ['claude-code'];
|
|
3398
|
+
})();
|
|
3399
|
+
const claudeEnabled = enabled.includes('claude-code');
|
|
3400
|
+
// Install telegram-reply.sh if Telegram is configured (framework-neutral —
|
|
3401
|
+
// installs to both `.claude/scripts/` for Claude and `.instar/scripts/`
|
|
3402
|
+
// for runtime-neutral after v1.0.10 Gap 4)
|
|
3316
3403
|
if (isTelegramConfigured(stateDir)) {
|
|
3317
3404
|
installTelegramRelay(projectDir, port);
|
|
3318
3405
|
}
|
|
3319
|
-
// Install whatsapp-reply.sh if WhatsApp is configured
|
|
3406
|
+
// Install whatsapp-reply.sh if WhatsApp is configured (lives in
|
|
3407
|
+
// `.instar/scripts/` already per init.ts; framework-neutral)
|
|
3320
3408
|
if (isWhatsAppConfigured(stateDir)) {
|
|
3321
3409
|
installWhatsAppRelay(projectDir, port);
|
|
3322
3410
|
}
|
|
3323
|
-
//
|
|
3324
|
-
|
|
3325
|
-
//
|
|
3326
|
-
|
|
3327
|
-
|
|
3411
|
+
// smart-fetch.py + git-sync-gate.sh both target `.claude/scripts/` and
|
|
3412
|
+
// are Claude-Code-specific (the agentic-web fetch script and the
|
|
3413
|
+
// pre-`git push` screening hook). Gate on claude-code enabled.
|
|
3414
|
+
if (claudeEnabled) {
|
|
3415
|
+
installSmartFetch(projectDir);
|
|
3416
|
+
installGitSyncGate(projectDir);
|
|
3417
|
+
}
|
|
3418
|
+
// Always install serendipity-capture.sh (lives in `.instar/scripts/`,
|
|
3419
|
+
// framework-neutral).
|
|
3328
3420
|
installSerendipityCapture(projectDir);
|
|
3329
3421
|
}
|
|
3330
3422
|
/**
|