cognitive-core 0.2.1 → 0.2.2
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/dist/atlas.d.ts +10 -0
- package/dist/atlas.d.ts.map +1 -1
- package/dist/atlas.js +65 -0
- package/dist/atlas.js.map +1 -1
- package/dist/learning/pipeline.d.ts +4 -31
- package/dist/learning/pipeline.d.ts.map +1 -1
- package/dist/learning/pipeline.js +12 -64
- package/dist/learning/pipeline.js.map +1 -1
- package/dist/memory/curated-loader.d.ts +21 -4
- package/dist/memory/curated-loader.d.ts.map +1 -1
- package/dist/memory/curated-loader.js +53 -16
- package/dist/memory/curated-loader.js.map +1 -1
- package/dist/memory/index.d.ts +2 -1
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +3 -1
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/playbook.d.ts +6 -0
- package/dist/memory/playbook.d.ts.map +1 -1
- package/dist/memory/playbook.js +15 -0
- package/dist/memory/playbook.js.map +1 -1
- package/dist/memory/source-resolver.d.ts +120 -0
- package/dist/memory/source-resolver.d.ts.map +1 -0
- package/dist/memory/source-resolver.js +300 -0
- package/dist/memory/source-resolver.js.map +1 -0
- package/dist/types/config.d.ts +141 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +40 -0
- package/dist/types/config.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/workspace/types.d.ts +12 -54
- package/dist/workspace/types.d.ts.map +1 -1
- package/dist/workspace/types.js.map +1 -1
- package/package.json +2 -2
- package/playbooks/compound-engineering/adversarial-review.json +51 -0
- package/playbooks/compound-engineering/agent-native-architecture.json +59 -0
- package/playbooks/compound-engineering/agent-native-review.json +54 -0
- package/playbooks/compound-engineering/api-contract-review.json +52 -0
- package/playbooks/compound-engineering/brainstorm-requirements.json +55 -0
- package/playbooks/compound-engineering/bug-reproduction.json +62 -0
- package/playbooks/compound-engineering/confidence-calibration.json +49 -0
- package/playbooks/compound-engineering/correctness-review.json +49 -0
- package/playbooks/compound-engineering/data-migration-safety.json +59 -0
- package/playbooks/compound-engineering/deployment-verification.json +63 -0
- package/playbooks/compound-engineering/error-recovery-patterns.json +53 -0
- package/playbooks/compound-engineering/implementation-planning.json +64 -0
- package/playbooks/compound-engineering/issue-pattern-analysis.json +53 -0
- package/playbooks/compound-engineering/knowledge-compounding.json +63 -0
- package/playbooks/compound-engineering/learnings-research.json +54 -0
- package/playbooks/compound-engineering/maintainability-review.json +49 -0
- package/playbooks/compound-engineering/performance-review.json +54 -0
- package/playbooks/compound-engineering/plan-adversarial-review.json +56 -0
- package/playbooks/compound-engineering/plan-feasibility-review.json +56 -0
- package/playbooks/compound-engineering/project-standards-review.json +52 -0
- package/playbooks/compound-engineering/reliability-review.json +53 -0
- package/playbooks/compound-engineering/review-orchestration.json +64 -0
- package/playbooks/compound-engineering/security-review.json +54 -0
- package/playbooks/compound-engineering/systematic-execution.json +64 -0
- package/playbooks/compound-engineering/testing-review.json +50 -0
- package/src/atlas.ts +96 -0
- package/src/memory/curated-loader.ts +69 -16
- package/src/memory/index.ts +16 -0
- package/src/memory/playbook.ts +19 -0
- package/src/memory/source-resolver.ts +422 -0
- package/src/types/config.ts +46 -0
- package/src/types/index.ts +4 -0
- package/src/workspace/types.ts +22 -78
- package/tests/integration/curated-sources-e2e.test.ts +502 -0
- package/tests/memory/compound-engineering-seed.test.ts +338 -0
- package/tests/memory/curated-loader-extended.test.ts +225 -0
- package/tests/memory/playbook-quality-validation.test.ts +430 -0
- package/tests/memory/source-resolver.test.ts +700 -0
- package/.claude/settings.local.json +0 -11
- package/dist/learning/llm-extractor.d.ts +0 -88
- package/dist/learning/llm-extractor.d.ts.map +0 -1
- package/dist/learning/llm-extractor.js +0 -372
- package/dist/learning/llm-extractor.js.map +0 -1
- package/dist/learning/loop-coordinator.d.ts +0 -61
- package/dist/learning/loop-coordinator.d.ts.map +0 -1
- package/dist/learning/loop-coordinator.js +0 -96
- package/dist/learning/loop-coordinator.js.map +0 -1
- package/references/agent-workspace/CLAUDE.md +0 -74
- package/references/agent-workspace/README.md +0 -587
- package/references/agent-workspace/media/banner.png +0 -0
- package/references/agent-workspace/package-lock.json +0 -2061
- package/references/agent-workspace/package.json +0 -54
- package/references/agent-workspace/src/handle.ts +0 -122
- package/references/agent-workspace/src/index.ts +0 -32
- package/references/agent-workspace/src/manager.ts +0 -102
- package/references/agent-workspace/src/readers/json.ts +0 -71
- package/references/agent-workspace/src/readers/markdown.ts +0 -37
- package/references/agent-workspace/src/readers/raw.ts +0 -27
- package/references/agent-workspace/src/types.ts +0 -68
- package/references/agent-workspace/src/validation.ts +0 -93
- package/references/agent-workspace/src/writers/json.ts +0 -17
- package/references/agent-workspace/src/writers/markdown.ts +0 -27
- package/references/agent-workspace/src/writers/raw.ts +0 -22
- package/references/agent-workspace/tests/errors.test.ts +0 -652
- package/references/agent-workspace/tests/handle.test.ts +0 -144
- package/references/agent-workspace/tests/manager.test.ts +0 -124
- package/references/agent-workspace/tests/readers.test.ts +0 -205
- package/references/agent-workspace/tests/validation.test.ts +0 -196
- package/references/agent-workspace/tests/writers.test.ts +0 -108
- package/references/agent-workspace/tsconfig.json +0 -20
- package/references/agent-workspace/tsup.config.ts +0 -9
- package/references/minimem/.claude/settings.json +0 -7
- package/references/minimem/.sudocode/issues.jsonl +0 -18
- package/references/minimem/.sudocode/specs.jsonl +0 -1
- package/references/minimem/CLAUDE.md +0 -310
- package/references/minimem/README.md +0 -556
- package/references/minimem/claude-plugin/.claude-plugin/plugin.json +0 -10
- package/references/minimem/claude-plugin/.mcp.json +0 -7
- package/references/minimem/claude-plugin/README.md +0 -158
- package/references/minimem/claude-plugin/commands/recall.md +0 -47
- package/references/minimem/claude-plugin/commands/remember.md +0 -41
- package/references/minimem/claude-plugin/hooks/__tests__/hooks.test.ts +0 -272
- package/references/minimem/claude-plugin/hooks/hooks.json +0 -27
- package/references/minimem/claude-plugin/hooks/session-end.sh +0 -86
- package/references/minimem/claude-plugin/hooks/session-start.sh +0 -85
- package/references/minimem/claude-plugin/skills/memory/SKILL.md +0 -108
- package/references/minimem/package-lock.json +0 -5373
- package/references/minimem/package.json +0 -60
- package/references/minimem/scripts/postbuild.js +0 -35
- package/references/minimem/src/__tests__/edge-cases.test.ts +0 -371
- package/references/minimem/src/__tests__/errors.test.ts +0 -265
- package/references/minimem/src/__tests__/helpers.ts +0 -199
- package/references/minimem/src/__tests__/internal.test.ts +0 -407
- package/references/minimem/src/__tests__/knowledge.test.ts +0 -287
- package/references/minimem/src/__tests__/minimem.integration.test.ts +0 -1127
- package/references/minimem/src/__tests__/session.test.ts +0 -190
- package/references/minimem/src/cli/__tests__/commands.test.ts +0 -759
- package/references/minimem/src/cli/commands/__tests__/conflicts.test.ts +0 -141
- package/references/minimem/src/cli/commands/append.ts +0 -76
- package/references/minimem/src/cli/commands/config.ts +0 -262
- package/references/minimem/src/cli/commands/conflicts.ts +0 -413
- package/references/minimem/src/cli/commands/daemon.ts +0 -169
- package/references/minimem/src/cli/commands/index.ts +0 -12
- package/references/minimem/src/cli/commands/init.ts +0 -88
- package/references/minimem/src/cli/commands/mcp.ts +0 -177
- package/references/minimem/src/cli/commands/push-pull.ts +0 -213
- package/references/minimem/src/cli/commands/search.ts +0 -158
- package/references/minimem/src/cli/commands/status.ts +0 -84
- package/references/minimem/src/cli/commands/sync-init.ts +0 -290
- package/references/minimem/src/cli/commands/sync.ts +0 -70
- package/references/minimem/src/cli/commands/upsert.ts +0 -197
- package/references/minimem/src/cli/config.ts +0 -584
- package/references/minimem/src/cli/index.ts +0 -264
- package/references/minimem/src/cli/shared.ts +0 -161
- package/references/minimem/src/cli/sync/__tests__/central.test.ts +0 -152
- package/references/minimem/src/cli/sync/__tests__/conflicts.test.ts +0 -209
- package/references/minimem/src/cli/sync/__tests__/daemon.test.ts +0 -118
- package/references/minimem/src/cli/sync/__tests__/detection.test.ts +0 -207
- package/references/minimem/src/cli/sync/__tests__/integration.test.ts +0 -476
- package/references/minimem/src/cli/sync/__tests__/registry.test.ts +0 -363
- package/references/minimem/src/cli/sync/__tests__/state.test.ts +0 -255
- package/references/minimem/src/cli/sync/__tests__/validation.test.ts +0 -193
- package/references/minimem/src/cli/sync/__tests__/watcher.test.ts +0 -178
- package/references/minimem/src/cli/sync/central.ts +0 -292
- package/references/minimem/src/cli/sync/conflicts.ts +0 -204
- package/references/minimem/src/cli/sync/daemon.ts +0 -407
- package/references/minimem/src/cli/sync/detection.ts +0 -138
- package/references/minimem/src/cli/sync/index.ts +0 -107
- package/references/minimem/src/cli/sync/operations.ts +0 -373
- package/references/minimem/src/cli/sync/registry.ts +0 -279
- package/references/minimem/src/cli/sync/state.ts +0 -355
- package/references/minimem/src/cli/sync/validation.ts +0 -206
- package/references/minimem/src/cli/sync/watcher.ts +0 -234
- package/references/minimem/src/cli/version.ts +0 -34
- package/references/minimem/src/core/index.ts +0 -9
- package/references/minimem/src/core/indexer.ts +0 -628
- package/references/minimem/src/core/searcher.ts +0 -221
- package/references/minimem/src/db/schema.ts +0 -183
- package/references/minimem/src/db/sqlite-vec.ts +0 -24
- package/references/minimem/src/embeddings/__tests__/embeddings.test.ts +0 -431
- package/references/minimem/src/embeddings/batch-gemini.ts +0 -392
- package/references/minimem/src/embeddings/batch-openai.ts +0 -409
- package/references/minimem/src/embeddings/embeddings.ts +0 -434
- package/references/minimem/src/index.ts +0 -109
- package/references/minimem/src/internal.ts +0 -299
- package/references/minimem/src/minimem.ts +0 -1276
- package/references/minimem/src/search/__tests__/hybrid.test.ts +0 -247
- package/references/minimem/src/search/graph.ts +0 -234
- package/references/minimem/src/search/hybrid.ts +0 -151
- package/references/minimem/src/search/search.ts +0 -256
- package/references/minimem/src/server/__tests__/mcp.test.ts +0 -341
- package/references/minimem/src/server/__tests__/tools.test.ts +0 -364
- package/references/minimem/src/server/mcp.ts +0 -326
- package/references/minimem/src/server/tools.ts +0 -720
- package/references/minimem/src/session.ts +0 -460
- package/references/minimem/tsconfig.json +0 -19
- package/references/minimem/tsup.config.ts +0 -26
- package/references/minimem/vitest.config.ts +0 -24
- package/references/sessionlog/.husky/pre-commit +0 -1
- package/references/sessionlog/.lintstagedrc.json +0 -4
- package/references/sessionlog/.prettierignore +0 -4
- package/references/sessionlog/.prettierrc.json +0 -11
- package/references/sessionlog/LICENSE +0 -21
- package/references/sessionlog/README.md +0 -453
- package/references/sessionlog/eslint.config.js +0 -58
- package/references/sessionlog/package-lock.json +0 -3672
- package/references/sessionlog/package.json +0 -65
- package/references/sessionlog/src/__tests__/agent-hooks.test.ts +0 -570
- package/references/sessionlog/src/__tests__/agent-registry.test.ts +0 -127
- package/references/sessionlog/src/__tests__/claude-code-hooks.test.ts +0 -225
- package/references/sessionlog/src/__tests__/claude-generator.test.ts +0 -46
- package/references/sessionlog/src/__tests__/commit-msg.test.ts +0 -86
- package/references/sessionlog/src/__tests__/cursor-agent.test.ts +0 -224
- package/references/sessionlog/src/__tests__/e2e-live.test.ts +0 -890
- package/references/sessionlog/src/__tests__/event-log.test.ts +0 -183
- package/references/sessionlog/src/__tests__/flush-sentinel.test.ts +0 -105
- package/references/sessionlog/src/__tests__/gemini-agent.test.ts +0 -375
- package/references/sessionlog/src/__tests__/git-hooks.test.ts +0 -78
- package/references/sessionlog/src/__tests__/hook-managers.test.ts +0 -121
- package/references/sessionlog/src/__tests__/lifecycle-tasks.test.ts +0 -759
- package/references/sessionlog/src/__tests__/opencode-agent.test.ts +0 -338
- package/references/sessionlog/src/__tests__/redaction.test.ts +0 -136
- package/references/sessionlog/src/__tests__/session-repo.test.ts +0 -353
- package/references/sessionlog/src/__tests__/session-store.test.ts +0 -166
- package/references/sessionlog/src/__tests__/setup-ccweb.test.ts +0 -466
- package/references/sessionlog/src/__tests__/skill-live.test.ts +0 -461
- package/references/sessionlog/src/__tests__/summarize.test.ts +0 -348
- package/references/sessionlog/src/__tests__/task-plan-e2e.test.ts +0 -610
- package/references/sessionlog/src/__tests__/task-plan-live.test.ts +0 -632
- package/references/sessionlog/src/__tests__/transcript-timestamp.test.ts +0 -121
- package/references/sessionlog/src/__tests__/types.test.ts +0 -166
- package/references/sessionlog/src/__tests__/utils.test.ts +0 -333
- package/references/sessionlog/src/__tests__/validation.test.ts +0 -103
- package/references/sessionlog/src/__tests__/worktree.test.ts +0 -57
- package/references/sessionlog/src/agent/agents/claude-code.ts +0 -1089
- package/references/sessionlog/src/agent/agents/cursor.ts +0 -361
- package/references/sessionlog/src/agent/agents/gemini-cli.ts +0 -632
- package/references/sessionlog/src/agent/agents/opencode.ts +0 -540
- package/references/sessionlog/src/agent/registry.ts +0 -143
- package/references/sessionlog/src/agent/session-types.ts +0 -113
- package/references/sessionlog/src/agent/types.ts +0 -220
- package/references/sessionlog/src/cli.ts +0 -597
- package/references/sessionlog/src/commands/clean.ts +0 -133
- package/references/sessionlog/src/commands/disable.ts +0 -84
- package/references/sessionlog/src/commands/doctor.ts +0 -145
- package/references/sessionlog/src/commands/enable.ts +0 -202
- package/references/sessionlog/src/commands/explain.ts +0 -261
- package/references/sessionlog/src/commands/reset.ts +0 -105
- package/references/sessionlog/src/commands/resume.ts +0 -180
- package/references/sessionlog/src/commands/rewind.ts +0 -195
- package/references/sessionlog/src/commands/setup-ccweb.ts +0 -275
- package/references/sessionlog/src/commands/status.ts +0 -172
- package/references/sessionlog/src/config.ts +0 -165
- package/references/sessionlog/src/events/event-log.ts +0 -126
- package/references/sessionlog/src/git-operations.ts +0 -558
- package/references/sessionlog/src/hooks/git-hooks.ts +0 -165
- package/references/sessionlog/src/hooks/lifecycle.ts +0 -391
- package/references/sessionlog/src/index.ts +0 -650
- package/references/sessionlog/src/security/redaction.ts +0 -283
- package/references/sessionlog/src/session/state-machine.ts +0 -452
- package/references/sessionlog/src/store/checkpoint-store.ts +0 -509
- package/references/sessionlog/src/store/native-store.ts +0 -173
- package/references/sessionlog/src/store/provider-types.ts +0 -99
- package/references/sessionlog/src/store/session-store.ts +0 -266
- package/references/sessionlog/src/strategy/attribution.ts +0 -296
- package/references/sessionlog/src/strategy/common.ts +0 -207
- package/references/sessionlog/src/strategy/content-overlap.ts +0 -228
- package/references/sessionlog/src/strategy/manual-commit.ts +0 -988
- package/references/sessionlog/src/strategy/types.ts +0 -279
- package/references/sessionlog/src/summarize/claude-generator.ts +0 -115
- package/references/sessionlog/src/summarize/summarize.ts +0 -432
- package/references/sessionlog/src/types.ts +0 -508
- package/references/sessionlog/src/utils/chunk-files.ts +0 -49
- package/references/sessionlog/src/utils/commit-message.ts +0 -65
- package/references/sessionlog/src/utils/detect-agent.ts +0 -36
- package/references/sessionlog/src/utils/hook-managers.ts +0 -125
- package/references/sessionlog/src/utils/ide-tags.ts +0 -32
- package/references/sessionlog/src/utils/paths.ts +0 -79
- package/references/sessionlog/src/utils/preview-rewind.ts +0 -80
- package/references/sessionlog/src/utils/rewind-conflict.ts +0 -121
- package/references/sessionlog/src/utils/shadow-branch.ts +0 -109
- package/references/sessionlog/src/utils/string-utils.ts +0 -46
- package/references/sessionlog/src/utils/todo-extract.ts +0 -188
- package/references/sessionlog/src/utils/trailers.ts +0 -187
- package/references/sessionlog/src/utils/transcript-parse.ts +0 -177
- package/references/sessionlog/src/utils/transcript-timestamp.ts +0 -59
- package/references/sessionlog/src/utils/tree-ops.ts +0 -219
- package/references/sessionlog/src/utils/tty.ts +0 -72
- package/references/sessionlog/src/utils/validation.ts +0 -65
- package/references/sessionlog/src/utils/worktree.ts +0 -58
- package/references/sessionlog/src/wire-types.ts +0 -59
- package/references/sessionlog/templates/setup-env.sh +0 -153
- package/references/sessionlog/tsconfig.json +0 -18
- package/references/sessionlog/vitest.config.ts +0 -12
- package/references/skill-tree/.claude/settings.json +0 -6
- package/references/skill-tree/.sudocode/issues.jsonl +0 -19
- package/references/skill-tree/.sudocode/specs.jsonl +0 -3
- package/references/skill-tree/CLAUDE.md +0 -126
- package/references/skill-tree/README.md +0 -372
- package/references/skill-tree/docs/GAPS_v1.md +0 -221
- package/references/skill-tree/docs/INTEGRATION_PLAN.md +0 -467
- package/references/skill-tree/docs/TODOS.md +0 -91
- package/references/skill-tree/docs/anthropic_skill_guide.md +0 -1364
- package/references/skill-tree/docs/design/federated-skill-trees.md +0 -524
- package/references/skill-tree/docs/design/multi-agent-sync.md +0 -759
- package/references/skill-tree/docs/scraper/BRAINSTORM.md +0 -583
- package/references/skill-tree/docs/scraper/POC_PLAN.md +0 -420
- package/references/skill-tree/docs/scraper/README.md +0 -170
- package/references/skill-tree/examples/basic-usage.ts +0 -164
- package/references/skill-tree/package-lock.json +0 -1852
- package/references/skill-tree/package.json +0 -66
- package/references/skill-tree/scraper/README.md +0 -123
- package/references/skill-tree/scraper/docs/DESIGN.md +0 -683
- package/references/skill-tree/scraper/docs/PLAN.md +0 -336
- package/references/skill-tree/scraper/drizzle.config.ts +0 -10
- package/references/skill-tree/scraper/package-lock.json +0 -6329
- package/references/skill-tree/scraper/package.json +0 -68
- package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-description.md +0 -7
- package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-name.md +0 -7
- package/references/skill-tree/scraper/test/fixtures/minimal-skill/SKILL.md +0 -27
- package/references/skill-tree/scraper/test/fixtures/skill-json/SKILL.json +0 -21
- package/references/skill-tree/scraper/test/fixtures/skill-with-meta/SKILL.md +0 -54
- package/references/skill-tree/scraper/test/fixtures/skill-with-meta/_meta.json +0 -24
- package/references/skill-tree/scraper/test/fixtures/valid-skill/SKILL.md +0 -93
- package/references/skill-tree/scraper/test/fixtures/valid-skill/_meta.json +0 -22
- package/references/skill-tree/scraper/tsup.config.ts +0 -14
- package/references/skill-tree/scraper/vitest.config.ts +0 -17
- package/references/skill-tree/scripts/convert-to-vitest.ts +0 -166
- package/references/skill-tree/skills/skill-writer/SKILL.md +0 -339
- package/references/skill-tree/skills/skill-writer/references/examples.md +0 -326
- package/references/skill-tree/skills/skill-writer/references/patterns.md +0 -210
- package/references/skill-tree/skills/skill-writer/references/quality-checklist.md +0 -123
- package/references/skill-tree/test/run-all.ts +0 -106
- package/references/skill-tree/test/utils.ts +0 -128
- package/references/skill-tree/vitest.config.ts +0 -16
|
@@ -1,466 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for setup-ccweb command
|
|
3
|
-
*
|
|
4
|
-
* Covers: setupCcweb function — settings creation, script creation,
|
|
5
|
-
* idempotency, --force overwrite, push prefix customization, prefix
|
|
6
|
-
* preservation on --force, non-git-repo error, and directory creation.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
10
|
-
import * as fs from 'node:fs';
|
|
11
|
-
import * as path from 'node:path';
|
|
12
|
-
import * as os from 'node:os';
|
|
13
|
-
import { execSync } from 'node:child_process';
|
|
14
|
-
import { setupCcweb } from '../commands/setup-ccweb.js';
|
|
15
|
-
|
|
16
|
-
// ============================================================================
|
|
17
|
-
// Helpers
|
|
18
|
-
// ============================================================================
|
|
19
|
-
|
|
20
|
-
function initRepo(dir: string): void {
|
|
21
|
-
execSync('git init', { cwd: dir, stdio: 'pipe' });
|
|
22
|
-
execSync('git config user.email "test@test.com"', { cwd: dir, stdio: 'pipe' });
|
|
23
|
-
execSync('git config user.name "Test"', { cwd: dir, stdio: 'pipe' });
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function readJSON(filePath: string): Record<string, unknown> {
|
|
27
|
-
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// ============================================================================
|
|
31
|
-
// Tests
|
|
32
|
-
// ============================================================================
|
|
33
|
-
|
|
34
|
-
describe('setup-ccweb', () => {
|
|
35
|
-
let tmpDir: string;
|
|
36
|
-
|
|
37
|
-
beforeEach(() => {
|
|
38
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'setup-ccweb-'));
|
|
39
|
-
initRepo(tmpDir);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
afterEach(() => {
|
|
43
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// --------------------------------------------------------------------------
|
|
47
|
-
// Basic creation
|
|
48
|
-
// --------------------------------------------------------------------------
|
|
49
|
-
|
|
50
|
-
describe('fresh setup', () => {
|
|
51
|
-
it('should create .claude/settings.json with SessionStart hook', async () => {
|
|
52
|
-
const result = await setupCcweb({ cwd: tmpDir });
|
|
53
|
-
|
|
54
|
-
expect(result.success).toBe(true);
|
|
55
|
-
expect(result.settingsCreated).toBe(true);
|
|
56
|
-
|
|
57
|
-
const settingsPath = path.join(tmpDir, '.claude', 'settings.json');
|
|
58
|
-
expect(fs.existsSync(settingsPath)).toBe(true);
|
|
59
|
-
|
|
60
|
-
const settings = readJSON(settingsPath) as {
|
|
61
|
-
hooks: { SessionStart: Array<{ hooks: Array<{ type: string; command: string }> }> };
|
|
62
|
-
};
|
|
63
|
-
expect(settings.hooks).toBeDefined();
|
|
64
|
-
expect(settings.hooks.SessionStart).toHaveLength(1);
|
|
65
|
-
expect(settings.hooks.SessionStart[0].hooks[0].type).toBe('command');
|
|
66
|
-
expect(settings.hooks.SessionStart[0].hooks[0].command).toBe(
|
|
67
|
-
'sh .claude/scripts/setup-env.sh',
|
|
68
|
-
);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('should add bash permission for the setup script', async () => {
|
|
72
|
-
await setupCcweb({ cwd: tmpDir });
|
|
73
|
-
|
|
74
|
-
const settingsPath = path.join(tmpDir, '.claude', 'settings.json');
|
|
75
|
-
const settings = readJSON(settingsPath) as {
|
|
76
|
-
permissions: { allow: string[] };
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
expect(settings.permissions).toBeDefined();
|
|
80
|
-
expect(settings.permissions.allow).toContain('Bash(sh .claude/scripts/setup-env.sh)');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should create .claude/scripts/setup-env.sh', async () => {
|
|
84
|
-
const result = await setupCcweb({ cwd: tmpDir });
|
|
85
|
-
|
|
86
|
-
expect(result.success).toBe(true);
|
|
87
|
-
expect(result.scriptCreated).toBe(true);
|
|
88
|
-
|
|
89
|
-
const scriptPath = path.join(tmpDir, '.claude', 'scripts', 'setup-env.sh');
|
|
90
|
-
expect(fs.existsSync(scriptPath)).toBe(true);
|
|
91
|
-
|
|
92
|
-
const content = fs.readFileSync(scriptPath, 'utf-8');
|
|
93
|
-
expect(content).toContain('#!/bin/sh');
|
|
94
|
-
expect(content).toContain('CLAUDE_CODE_REMOTE');
|
|
95
|
-
expect(content).toContain('npm install -g sessionlog');
|
|
96
|
-
expect(content).toContain('sessionlog enable --agent claude-code');
|
|
97
|
-
expect(content).toContain('GITHUB_TOKEN');
|
|
98
|
-
expect(content).toContain('sessionlog-ccweb-push-filter');
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('should make setup-env.sh executable', async () => {
|
|
102
|
-
await setupCcweb({ cwd: tmpDir });
|
|
103
|
-
|
|
104
|
-
const scriptPath = path.join(tmpDir, '.claude', 'scripts', 'setup-env.sh');
|
|
105
|
-
const stat = fs.statSync(scriptPath);
|
|
106
|
-
// Check owner execute bit
|
|
107
|
-
expect(stat.mode & 0o100).toBeTruthy();
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should create .claude/scripts/ directory', async () => {
|
|
111
|
-
await setupCcweb({ cwd: tmpDir });
|
|
112
|
-
|
|
113
|
-
const scriptsDir = path.join(tmpDir, '.claude', 'scripts');
|
|
114
|
-
expect(fs.existsSync(scriptsDir)).toBe(true);
|
|
115
|
-
expect(fs.statSync(scriptsDir).isDirectory()).toBe(true);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('should return no errors on fresh setup', async () => {
|
|
119
|
-
const result = await setupCcweb({ cwd: tmpDir });
|
|
120
|
-
|
|
121
|
-
expect(result.success).toBe(true);
|
|
122
|
-
expect(result.errors).toHaveLength(0);
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// --------------------------------------------------------------------------
|
|
127
|
-
// Default push prefixes
|
|
128
|
-
// --------------------------------------------------------------------------
|
|
129
|
-
|
|
130
|
-
describe('push prefixes', () => {
|
|
131
|
-
it('should use default push prefixes (sessionlog/ claude/)', async () => {
|
|
132
|
-
await setupCcweb({ cwd: tmpDir });
|
|
133
|
-
|
|
134
|
-
const scriptPath = path.join(tmpDir, '.claude', 'scripts', 'setup-env.sh');
|
|
135
|
-
const content = fs.readFileSync(scriptPath, 'utf-8');
|
|
136
|
-
expect(content).toContain('ALLOWED_PUSH_PREFIXES="sessionlog/ claude/"');
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('should use custom push prefixes when specified', async () => {
|
|
140
|
-
await setupCcweb({ cwd: tmpDir, pushPrefixes: 'sessionlog/ my-prefix/ other/' });
|
|
141
|
-
|
|
142
|
-
const scriptPath = path.join(tmpDir, '.claude', 'scripts', 'setup-env.sh');
|
|
143
|
-
const content = fs.readFileSync(scriptPath, 'utf-8');
|
|
144
|
-
expect(content).toContain('ALLOWED_PUSH_PREFIXES="sessionlog/ my-prefix/ other/"');
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('should preserve user-customized prefixes on --force without explicit prefixes', async () => {
|
|
148
|
-
// First setup with custom prefixes
|
|
149
|
-
await setupCcweb({ cwd: tmpDir, pushPrefixes: 'custom/ special/' });
|
|
150
|
-
|
|
151
|
-
// Force reinstall without specifying prefixes
|
|
152
|
-
const result = await setupCcweb({ cwd: tmpDir, force: true });
|
|
153
|
-
expect(result.scriptCreated).toBe(true);
|
|
154
|
-
|
|
155
|
-
const scriptPath = path.join(tmpDir, '.claude', 'scripts', 'setup-env.sh');
|
|
156
|
-
const content = fs.readFileSync(scriptPath, 'utf-8');
|
|
157
|
-
expect(content).toContain('ALLOWED_PUSH_PREFIXES="custom/ special/"');
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('should override preserved prefixes when --force with explicit prefixes', async () => {
|
|
161
|
-
// First setup with custom prefixes
|
|
162
|
-
await setupCcweb({ cwd: tmpDir, pushPrefixes: 'custom/ special/' });
|
|
163
|
-
|
|
164
|
-
// Force reinstall with new explicit prefixes
|
|
165
|
-
await setupCcweb({ cwd: tmpDir, force: true, pushPrefixes: 'new-prefix/' });
|
|
166
|
-
|
|
167
|
-
const scriptPath = path.join(tmpDir, '.claude', 'scripts', 'setup-env.sh');
|
|
168
|
-
const content = fs.readFileSync(scriptPath, 'utf-8');
|
|
169
|
-
expect(content).toContain('ALLOWED_PUSH_PREFIXES="new-prefix/"');
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// --------------------------------------------------------------------------
|
|
174
|
-
// Idempotency
|
|
175
|
-
// --------------------------------------------------------------------------
|
|
176
|
-
|
|
177
|
-
describe('idempotency', () => {
|
|
178
|
-
it('should not overwrite settings on second run', async () => {
|
|
179
|
-
await setupCcweb({ cwd: tmpDir });
|
|
180
|
-
const result = await setupCcweb({ cwd: tmpDir });
|
|
181
|
-
|
|
182
|
-
expect(result.success).toBe(true);
|
|
183
|
-
expect(result.settingsCreated).toBe(false);
|
|
184
|
-
expect(result.scriptCreated).toBe(false);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it('should not duplicate SessionStart hooks on second run', async () => {
|
|
188
|
-
await setupCcweb({ cwd: tmpDir });
|
|
189
|
-
await setupCcweb({ cwd: tmpDir });
|
|
190
|
-
|
|
191
|
-
const settingsPath = path.join(tmpDir, '.claude', 'settings.json');
|
|
192
|
-
const settings = readJSON(settingsPath) as {
|
|
193
|
-
hooks: { SessionStart: unknown[] };
|
|
194
|
-
};
|
|
195
|
-
expect(settings.hooks.SessionStart).toHaveLength(1);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it('should not duplicate bash permission on second run', async () => {
|
|
199
|
-
await setupCcweb({ cwd: tmpDir });
|
|
200
|
-
await setupCcweb({ cwd: tmpDir });
|
|
201
|
-
|
|
202
|
-
const settingsPath = path.join(tmpDir, '.claude', 'settings.json');
|
|
203
|
-
const settings = readJSON(settingsPath) as {
|
|
204
|
-
permissions: { allow: string[] };
|
|
205
|
-
};
|
|
206
|
-
const permCount = settings.permissions.allow.filter((p) => p.includes('setup-env.sh')).length;
|
|
207
|
-
expect(permCount).toBe(1);
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
// --------------------------------------------------------------------------
|
|
212
|
-
// --force flag
|
|
213
|
-
// --------------------------------------------------------------------------
|
|
214
|
-
|
|
215
|
-
describe('--force flag', () => {
|
|
216
|
-
it('should overwrite settings when force is true', async () => {
|
|
217
|
-
await setupCcweb({ cwd: tmpDir });
|
|
218
|
-
const result = await setupCcweb({ cwd: tmpDir, force: true });
|
|
219
|
-
|
|
220
|
-
expect(result.success).toBe(true);
|
|
221
|
-
expect(result.settingsCreated).toBe(true);
|
|
222
|
-
expect(result.scriptCreated).toBe(true);
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it('should not duplicate SessionStart hooks on force reinstall', async () => {
|
|
226
|
-
await setupCcweb({ cwd: tmpDir });
|
|
227
|
-
await setupCcweb({ cwd: tmpDir, force: true });
|
|
228
|
-
|
|
229
|
-
const settingsPath = path.join(tmpDir, '.claude', 'settings.json');
|
|
230
|
-
const settings = readJSON(settingsPath) as {
|
|
231
|
-
hooks: { SessionStart: unknown[] };
|
|
232
|
-
};
|
|
233
|
-
expect(settings.hooks.SessionStart).toHaveLength(1);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('should not duplicate bash permission on force reinstall', async () => {
|
|
237
|
-
await setupCcweb({ cwd: tmpDir });
|
|
238
|
-
await setupCcweb({ cwd: tmpDir, force: true });
|
|
239
|
-
|
|
240
|
-
const settingsPath = path.join(tmpDir, '.claude', 'settings.json');
|
|
241
|
-
const settings = readJSON(settingsPath) as {
|
|
242
|
-
permissions: { allow: string[] };
|
|
243
|
-
};
|
|
244
|
-
const permCount = settings.permissions.allow.filter((p) => p.includes('setup-env.sh')).length;
|
|
245
|
-
expect(permCount).toBe(1);
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
// --------------------------------------------------------------------------
|
|
250
|
-
// Existing settings preservation
|
|
251
|
-
// --------------------------------------------------------------------------
|
|
252
|
-
|
|
253
|
-
describe('existing settings preservation', () => {
|
|
254
|
-
it('should preserve existing hooks when adding SessionStart', async () => {
|
|
255
|
-
const claudeDir = path.join(tmpDir, '.claude');
|
|
256
|
-
fs.mkdirSync(claudeDir, { recursive: true });
|
|
257
|
-
|
|
258
|
-
const existingSettings = {
|
|
259
|
-
hooks: {
|
|
260
|
-
PreToolUse: [
|
|
261
|
-
{
|
|
262
|
-
matcher: 'Bash',
|
|
263
|
-
hooks: [{ type: 'command', command: 'echo test' }],
|
|
264
|
-
},
|
|
265
|
-
],
|
|
266
|
-
},
|
|
267
|
-
};
|
|
268
|
-
fs.writeFileSync(
|
|
269
|
-
path.join(claudeDir, 'settings.json'),
|
|
270
|
-
JSON.stringify(existingSettings, null, 2),
|
|
271
|
-
);
|
|
272
|
-
|
|
273
|
-
await setupCcweb({ cwd: tmpDir });
|
|
274
|
-
|
|
275
|
-
const settings = readJSON(path.join(claudeDir, 'settings.json')) as {
|
|
276
|
-
hooks: {
|
|
277
|
-
PreToolUse: unknown[];
|
|
278
|
-
SessionStart: unknown[];
|
|
279
|
-
};
|
|
280
|
-
};
|
|
281
|
-
expect(settings.hooks.PreToolUse).toHaveLength(1);
|
|
282
|
-
expect(settings.hooks.SessionStart).toHaveLength(1);
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
it('should preserve existing permissions when adding setup-env permission', async () => {
|
|
286
|
-
const claudeDir = path.join(tmpDir, '.claude');
|
|
287
|
-
fs.mkdirSync(claudeDir, { recursive: true });
|
|
288
|
-
|
|
289
|
-
const existingSettings = {
|
|
290
|
-
permissions: {
|
|
291
|
-
allow: ['Bash(npm test)'],
|
|
292
|
-
},
|
|
293
|
-
};
|
|
294
|
-
fs.writeFileSync(
|
|
295
|
-
path.join(claudeDir, 'settings.json'),
|
|
296
|
-
JSON.stringify(existingSettings, null, 2),
|
|
297
|
-
);
|
|
298
|
-
|
|
299
|
-
await setupCcweb({ cwd: tmpDir });
|
|
300
|
-
|
|
301
|
-
const settings = readJSON(path.join(claudeDir, 'settings.json')) as {
|
|
302
|
-
permissions: { allow: string[] };
|
|
303
|
-
};
|
|
304
|
-
expect(settings.permissions.allow).toContain('Bash(npm test)');
|
|
305
|
-
expect(settings.permissions.allow).toContain('Bash(sh .claude/scripts/setup-env.sh)');
|
|
306
|
-
expect(settings.permissions.allow).toHaveLength(2);
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
it('should preserve other top-level settings keys', async () => {
|
|
310
|
-
const claudeDir = path.join(tmpDir, '.claude');
|
|
311
|
-
fs.mkdirSync(claudeDir, { recursive: true });
|
|
312
|
-
|
|
313
|
-
const existingSettings = {
|
|
314
|
-
model: 'claude-sonnet-4-5-20250514',
|
|
315
|
-
customKey: 'customValue',
|
|
316
|
-
};
|
|
317
|
-
fs.writeFileSync(
|
|
318
|
-
path.join(claudeDir, 'settings.json'),
|
|
319
|
-
JSON.stringify(existingSettings, null, 2),
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
await setupCcweb({ cwd: tmpDir });
|
|
323
|
-
|
|
324
|
-
const settings = readJSON(path.join(claudeDir, 'settings.json')) as Record<string, unknown>;
|
|
325
|
-
expect(settings.model).toBe('claude-sonnet-4-5-20250514');
|
|
326
|
-
expect(settings.customKey).toBe('customValue');
|
|
327
|
-
expect(settings.hooks).toBeDefined();
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
// --------------------------------------------------------------------------
|
|
332
|
-
// Error handling
|
|
333
|
-
// --------------------------------------------------------------------------
|
|
334
|
-
|
|
335
|
-
describe('error handling', () => {
|
|
336
|
-
it('should fail when not in a git repository', async () => {
|
|
337
|
-
const nonGitDir = fs.mkdtempSync(path.join(os.tmpdir(), 'no-git-'));
|
|
338
|
-
|
|
339
|
-
try {
|
|
340
|
-
const result = await setupCcweb({ cwd: nonGitDir });
|
|
341
|
-
|
|
342
|
-
expect(result.success).toBe(false);
|
|
343
|
-
expect(result.settingsCreated).toBe(false);
|
|
344
|
-
expect(result.scriptCreated).toBe(false);
|
|
345
|
-
expect(result.errors).toContain('Not a git repository');
|
|
346
|
-
} finally {
|
|
347
|
-
fs.rmSync(nonGitDir, { recursive: true, force: true });
|
|
348
|
-
}
|
|
349
|
-
});
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
// --------------------------------------------------------------------------
|
|
353
|
-
// Script content validation
|
|
354
|
-
// --------------------------------------------------------------------------
|
|
355
|
-
|
|
356
|
-
describe('script content', () => {
|
|
357
|
-
it('should only run in remote Claude Code environments', async () => {
|
|
358
|
-
await setupCcweb({ cwd: tmpDir });
|
|
359
|
-
|
|
360
|
-
const scriptPath = path.join(tmpDir, '.claude', 'scripts', 'setup-env.sh');
|
|
361
|
-
const content = fs.readFileSync(scriptPath, 'utf-8');
|
|
362
|
-
expect(content).toContain('CLAUDE_CODE_REMOTE');
|
|
363
|
-
expect(content).toContain('exit 0');
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
it('should install sessionlog via npm', async () => {
|
|
367
|
-
await setupCcweb({ cwd: tmpDir });
|
|
368
|
-
|
|
369
|
-
const scriptPath = path.join(tmpDir, '.claude', 'scripts', 'setup-env.sh');
|
|
370
|
-
const content = fs.readFileSync(scriptPath, 'utf-8');
|
|
371
|
-
expect(content).toContain('npm install -g sessionlog');
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
it('should enable sessionlog with claude-code agent', async () => {
|
|
375
|
-
await setupCcweb({ cwd: tmpDir });
|
|
376
|
-
|
|
377
|
-
const scriptPath = path.join(tmpDir, '.claude', 'scripts', 'setup-env.sh');
|
|
378
|
-
const content = fs.readFileSync(scriptPath, 'utf-8');
|
|
379
|
-
expect(content).toContain('sessionlog enable --agent claude-code --local');
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
it('should configure GitHub direct-push access', async () => {
|
|
383
|
-
await setupCcweb({ cwd: tmpDir });
|
|
384
|
-
|
|
385
|
-
const scriptPath = path.join(tmpDir, '.claude', 'scripts', 'setup-env.sh');
|
|
386
|
-
const content = fs.readFileSync(scriptPath, 'utf-8');
|
|
387
|
-
expect(content).toContain('GITHUB_TOKEN');
|
|
388
|
-
expect(content).toContain('git remote set-url --push origin');
|
|
389
|
-
expect(content).toContain('x-access-token');
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
it('should install pre-push branch filter', async () => {
|
|
393
|
-
await setupCcweb({ cwd: tmpDir });
|
|
394
|
-
|
|
395
|
-
const scriptPath = path.join(tmpDir, '.claude', 'scripts', 'setup-env.sh');
|
|
396
|
-
const content = fs.readFileSync(scriptPath, 'utf-8');
|
|
397
|
-
expect(content).toContain('sessionlog-ccweb-push-filter');
|
|
398
|
-
expect(content).toContain('Blocked push to');
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
it('should start with proper shebang', async () => {
|
|
402
|
-
await setupCcweb({ cwd: tmpDir });
|
|
403
|
-
|
|
404
|
-
const scriptPath = path.join(tmpDir, '.claude', 'scripts', 'setup-env.sh');
|
|
405
|
-
const content = fs.readFileSync(scriptPath, 'utf-8');
|
|
406
|
-
expect(content.startsWith('#!/bin/sh')).toBe(true);
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
it('should use set -e for error handling', async () => {
|
|
410
|
-
await setupCcweb({ cwd: tmpDir });
|
|
411
|
-
|
|
412
|
-
const scriptPath = path.join(tmpDir, '.claude', 'scripts', 'setup-env.sh');
|
|
413
|
-
const content = fs.readFileSync(scriptPath, 'utf-8');
|
|
414
|
-
expect(content).toContain('set -e');
|
|
415
|
-
});
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
// --------------------------------------------------------------------------
|
|
419
|
-
// Settings JSON structure
|
|
420
|
-
// --------------------------------------------------------------------------
|
|
421
|
-
|
|
422
|
-
describe('settings structure', () => {
|
|
423
|
-
it('should use correct SessionStart hook format with matcher and hooks array', async () => {
|
|
424
|
-
await setupCcweb({ cwd: tmpDir });
|
|
425
|
-
|
|
426
|
-
const settingsPath = path.join(tmpDir, '.claude', 'settings.json');
|
|
427
|
-
const settings = readJSON(settingsPath) as {
|
|
428
|
-
hooks: {
|
|
429
|
-
SessionStart: Array<{
|
|
430
|
-
matcher: string;
|
|
431
|
-
hooks: Array<{ type: string; command: string }>;
|
|
432
|
-
}>;
|
|
433
|
-
};
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
const hook = settings.hooks.SessionStart[0];
|
|
437
|
-
expect(hook.matcher).toBe('');
|
|
438
|
-
expect(hook.hooks).toHaveLength(1);
|
|
439
|
-
expect(hook.hooks[0].type).toBe('command');
|
|
440
|
-
expect(hook.hooks[0].command).toBe('sh .claude/scripts/setup-env.sh');
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
it('should write valid JSON with trailing newline', async () => {
|
|
444
|
-
await setupCcweb({ cwd: tmpDir });
|
|
445
|
-
|
|
446
|
-
const settingsPath = path.join(tmpDir, '.claude', 'settings.json');
|
|
447
|
-
const raw = fs.readFileSync(settingsPath, 'utf-8');
|
|
448
|
-
|
|
449
|
-
// Should be valid JSON
|
|
450
|
-
expect(() => JSON.parse(raw)).not.toThrow();
|
|
451
|
-
|
|
452
|
-
// Should end with newline
|
|
453
|
-
expect(raw.endsWith('\n')).toBe(true);
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
it('should use pretty-printed JSON with 2-space indent', async () => {
|
|
457
|
-
await setupCcweb({ cwd: tmpDir });
|
|
458
|
-
|
|
459
|
-
const settingsPath = path.join(tmpDir, '.claude', 'settings.json');
|
|
460
|
-
const raw = fs.readFileSync(settingsPath, 'utf-8');
|
|
461
|
-
|
|
462
|
-
// Check that it has 2-space indentation
|
|
463
|
-
expect(raw).toContain(' "hooks"');
|
|
464
|
-
});
|
|
465
|
-
});
|
|
466
|
-
});
|