@sixfactors-ai/codeloop 0.1.1 → 0.2.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 +133 -71
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.js +125 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.js +35 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +77 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/publish.d.ts +2 -0
- package/dist/commands/publish.js +125 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/remove.d.ts +2 -0
- package/dist/commands/remove.js +31 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/search.d.ts +2 -0
- package/dist/commands/search.js +85 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +85 -11
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/update.js +10 -22
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/watch.d.ts +6 -0
- package/dist/commands/watch.js +111 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/__tests__/scaffold.test.js +24 -0
- package/dist/lib/__tests__/scaffold.test.js.map +1 -1
- package/dist/lib/__tests__/smoke.test.js +41 -3
- package/dist/lib/__tests__/smoke.test.js.map +1 -1
- package/dist/lib/__tests__/validate-config.test.d.ts +1 -0
- package/dist/lib/__tests__/validate-config.test.js +109 -0
- package/dist/lib/__tests__/validate-config.test.js.map +1 -0
- package/dist/lib/scaffold.js +32 -3
- package/dist/lib/scaffold.js.map +1 -1
- package/dist/registry/__tests__/installer.test.d.ts +1 -0
- package/dist/registry/__tests__/installer.test.js +122 -0
- package/dist/registry/__tests__/installer.test.js.map +1 -0
- package/dist/registry/__tests__/local-index.test.d.ts +1 -0
- package/dist/registry/__tests__/local-index.test.js +73 -0
- package/dist/registry/__tests__/local-index.test.js.map +1 -0
- package/dist/registry/__tests__/lockfile.test.d.ts +1 -0
- package/dist/registry/__tests__/lockfile.test.js +93 -0
- package/dist/registry/__tests__/lockfile.test.js.map +1 -0
- package/dist/registry/__tests__/security.test.d.ts +1 -0
- package/dist/registry/__tests__/security.test.js +100 -0
- package/dist/registry/__tests__/security.test.js.map +1 -0
- package/dist/registry/__tests__/skill-schema.test.d.ts +1 -0
- package/dist/registry/__tests__/skill-schema.test.js +102 -0
- package/dist/registry/__tests__/skill-schema.test.js.map +1 -0
- package/dist/registry/index.d.ts +7 -0
- package/dist/registry/index.js +8 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/installer.d.ts +30 -0
- package/dist/registry/installer.js +133 -0
- package/dist/registry/installer.js.map +1 -0
- package/dist/registry/local-index.d.ts +32 -0
- package/dist/registry/local-index.js +58 -0
- package/dist/registry/local-index.js.map +1 -0
- package/dist/registry/lockfile.d.ts +40 -0
- package/dist/registry/lockfile.js +85 -0
- package/dist/registry/lockfile.js.map +1 -0
- package/dist/registry/security.d.ts +25 -0
- package/dist/registry/security.js +100 -0
- package/dist/registry/security.js.map +1 -0
- package/dist/registry/skill-schema.d.ts +30 -0
- package/dist/registry/skill-schema.js +95 -0
- package/dist/registry/skill-schema.js.map +1 -0
- package/dist/ui/404.html +1 -1
- package/dist/ui/index.html +1 -1
- package/dist/ui/index.txt +1 -1
- package/dist/watch/__tests__/config.test.d.ts +1 -0
- package/dist/watch/__tests__/config.test.js +53 -0
- package/dist/watch/__tests__/config.test.js.map +1 -0
- package/dist/watch/__tests__/signals.test.d.ts +1 -0
- package/dist/watch/__tests__/signals.test.js +41 -0
- package/dist/watch/__tests__/signals.test.js.map +1 -0
- package/dist/watch/__tests__/triggers.test.d.ts +1 -0
- package/dist/watch/__tests__/triggers.test.js +92 -0
- package/dist/watch/__tests__/triggers.test.js.map +1 -0
- package/dist/watch/index.d.ts +21 -0
- package/dist/watch/index.js +88 -0
- package/dist/watch/index.js.map +1 -0
- package/dist/watch/reporter.d.ts +11 -0
- package/dist/watch/reporter.js +44 -0
- package/dist/watch/reporter.js.map +1 -0
- package/dist/watch/signals.d.ts +38 -0
- package/dist/watch/signals.js +119 -0
- package/dist/watch/signals.js.map +1 -0
- package/dist/watch/triggers.d.ts +10 -0
- package/dist/watch/triggers.js +67 -0
- package/dist/watch/triggers.js.map +1 -0
- package/package.json +3 -2
- package/registry/index.json +106 -0
- package/starters/generic.yaml +37 -0
- package/starters/go.yaml +39 -0
- package/starters/node-typescript.yaml +39 -0
- package/starters/python.yaml +42 -0
- package/templates/commands/debug.md +142 -0
- package/templates/commands/deploy.md +144 -0
- package/templates/commands/design.md +102 -0
- package/templates/commands/manage.md +1 -1
- package/templates/commands/plan.md +4 -3
- package/templates/commands/qa.md +155 -0
- package/templates/commands/ship.md +187 -0
- package/templates/commands/test.md +133 -0
- package/templates/seeds/go-gotchas.md +28 -0
- package/templates/seeds/go-patterns.md +22 -0
- package/templates/seeds/node-typescript-gotchas.md +30 -0
- package/templates/seeds/node-typescript-patterns.md +27 -0
- package/templates/seeds/python-gotchas.md +30 -0
- package/templates/seeds/python-patterns.md +19 -0
- package/templates/seeds/universal-gotchas.md +11 -0
- package/templates/seeds/universal-patterns.md +11 -0
- /package/dist/ui/_next/static/{uiiPJR68HihKQsXtPj0fm → Z7X6LpFN441Kvx1ZYF2iY}/_buildManifest.js +0 -0
- /package/dist/ui/_next/static/{uiiPJR68HihKQsXtPj0fm → Z7X6LpFN441Kvx1ZYF2iY}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lockfile management for installed skills.
|
|
3
|
+
* Tracks installed skills with versions, sources, and integrity hashes.
|
|
4
|
+
*/
|
|
5
|
+
export interface LockedSkill {
|
|
6
|
+
version: string;
|
|
7
|
+
source: string;
|
|
8
|
+
tier: 'trusted' | 'community' | 'unreviewed';
|
|
9
|
+
installed: string;
|
|
10
|
+
integrity: string;
|
|
11
|
+
files: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface Lockfile {
|
|
14
|
+
installed: Record<string, LockedSkill>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Compute sha256 integrity hash for skill content.
|
|
18
|
+
*/
|
|
19
|
+
export declare function computeIntegrity(content: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Load the lockfile from the project directory.
|
|
22
|
+
* Returns an empty lockfile if it doesn't exist.
|
|
23
|
+
*/
|
|
24
|
+
export declare function loadLockfile(projectDir: string): Lockfile;
|
|
25
|
+
/**
|
|
26
|
+
* Save the lockfile to the project directory.
|
|
27
|
+
*/
|
|
28
|
+
export declare function saveLockfile(projectDir: string, lockfile: Lockfile): void;
|
|
29
|
+
/**
|
|
30
|
+
* Add or update a skill in the lockfile.
|
|
31
|
+
*/
|
|
32
|
+
export declare function lockSkill(lockfile: Lockfile, name: string, entry: LockedSkill): Lockfile;
|
|
33
|
+
/**
|
|
34
|
+
* Remove a skill from the lockfile.
|
|
35
|
+
*/
|
|
36
|
+
export declare function unlockSkill(lockfile: Lockfile, name: string): Lockfile;
|
|
37
|
+
/**
|
|
38
|
+
* Check if a skill's installed files match their integrity hash.
|
|
39
|
+
*/
|
|
40
|
+
export declare function verifyIntegrity(projectDir: string, name: string, lockfile: Lockfile): boolean;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lockfile management for installed skills.
|
|
3
|
+
* Tracks installed skills with versions, sources, and integrity hashes.
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
6
|
+
import { join, dirname } from 'path';
|
|
7
|
+
import { createHash } from 'crypto';
|
|
8
|
+
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
9
|
+
const LOCKFILE_PATH = '.codeloop/skills.lock';
|
|
10
|
+
/**
|
|
11
|
+
* Compute sha256 integrity hash for skill content.
|
|
12
|
+
*/
|
|
13
|
+
export function computeIntegrity(content) {
|
|
14
|
+
return `sha256-${createHash('sha256').update(content, 'utf-8').digest('hex').slice(0, 12)}`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Load the lockfile from the project directory.
|
|
18
|
+
* Returns an empty lockfile if it doesn't exist.
|
|
19
|
+
*/
|
|
20
|
+
export function loadLockfile(projectDir) {
|
|
21
|
+
const lockPath = join(projectDir, LOCKFILE_PATH);
|
|
22
|
+
if (!existsSync(lockPath)) {
|
|
23
|
+
return { installed: {} };
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const content = readFileSync(lockPath, 'utf-8');
|
|
27
|
+
const parsed = parseYaml(content);
|
|
28
|
+
return {
|
|
29
|
+
installed: parsed?.installed ?? {},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return { installed: {} };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Save the lockfile to the project directory.
|
|
38
|
+
*/
|
|
39
|
+
export function saveLockfile(projectDir, lockfile) {
|
|
40
|
+
const lockPath = join(projectDir, LOCKFILE_PATH);
|
|
41
|
+
const lockDir = dirname(lockPath);
|
|
42
|
+
if (!existsSync(lockDir)) {
|
|
43
|
+
mkdirSync(lockDir, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
const content = stringifyYaml(lockfile, { lineWidth: 120 });
|
|
46
|
+
writeFileSync(lockPath, content, 'utf-8');
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Add or update a skill in the lockfile.
|
|
50
|
+
*/
|
|
51
|
+
export function lockSkill(lockfile, name, entry) {
|
|
52
|
+
return {
|
|
53
|
+
installed: {
|
|
54
|
+
...lockfile.installed,
|
|
55
|
+
[name]: entry,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Remove a skill from the lockfile.
|
|
61
|
+
*/
|
|
62
|
+
export function unlockSkill(lockfile, name) {
|
|
63
|
+
const { [name]: _, ...rest } = lockfile.installed;
|
|
64
|
+
return { installed: rest };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Check if a skill's installed files match their integrity hash.
|
|
68
|
+
*/
|
|
69
|
+
export function verifyIntegrity(projectDir, name, lockfile) {
|
|
70
|
+
const entry = lockfile.installed[name];
|
|
71
|
+
if (!entry)
|
|
72
|
+
return false;
|
|
73
|
+
// Check that at least one installed file exists and matches
|
|
74
|
+
for (const filePath of entry.files) {
|
|
75
|
+
const fullPath = join(projectDir, filePath);
|
|
76
|
+
if (!existsSync(fullPath))
|
|
77
|
+
return false;
|
|
78
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
79
|
+
const hash = computeIntegrity(content);
|
|
80
|
+
if (hash !== entry.integrity)
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=lockfile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lockfile.js","sourceRoot":"","sources":["../../src/registry/lockfile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security validator for skill files.
|
|
3
|
+
* Checks for dangerous patterns that could compromise the user's system.
|
|
4
|
+
*/
|
|
5
|
+
export interface SecurityFinding {
|
|
6
|
+
pattern: string;
|
|
7
|
+
message: string;
|
|
8
|
+
line: number;
|
|
9
|
+
severity: 'block' | 'warn';
|
|
10
|
+
}
|
|
11
|
+
export interface SecurityResult {
|
|
12
|
+
passed: boolean;
|
|
13
|
+
findings: SecurityFinding[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Validate a skill file's content for security issues.
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateSecurity(content: string, filename?: string): SecurityResult;
|
|
19
|
+
/**
|
|
20
|
+
* Validate a collection of skill files for total size.
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateTotalSize(files: Array<{
|
|
23
|
+
name: string;
|
|
24
|
+
content: string;
|
|
25
|
+
}>): SecurityFinding | null;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security validator for skill files.
|
|
3
|
+
* Checks for dangerous patterns that could compromise the user's system.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Patterns that BLOCK skill installation (hard gates).
|
|
7
|
+
*/
|
|
8
|
+
const BLOCKED_PATTERNS = [
|
|
9
|
+
// Arbitrary code execution
|
|
10
|
+
{ regex: /\bexec\s*\(/, message: 'Arbitrary code execution via exec()' },
|
|
11
|
+
{ regex: /\bspawn\s*\(/, message: 'Arbitrary code execution via spawn()' },
|
|
12
|
+
{ regex: /\beval\s*\(/, message: 'Arbitrary code execution via eval()' },
|
|
13
|
+
{ regex: /\bnew\s+Function\s*\(/, message: 'Arbitrary code execution via Function constructor' },
|
|
14
|
+
// Remote code injection
|
|
15
|
+
{ regex: /curl\s+.*\|\s*sh/, message: 'Pipe-to-shell attack (curl | sh)' },
|
|
16
|
+
{ regex: /curl\s+.*\|\s*bash/, message: 'Pipe-to-shell attack (curl | bash)' },
|
|
17
|
+
{ regex: /wget\s+.*\|\s*sh/, message: 'Pipe-to-shell attack (wget | sh)' },
|
|
18
|
+
{ regex: /wget\s+.*\|\s*bash/, message: 'Pipe-to-shell attack (wget | bash)' },
|
|
19
|
+
// Destructive system operations
|
|
20
|
+
{ regex: /rm\s+-rf\s+\//, message: 'Destructive system operation (rm -rf /)' },
|
|
21
|
+
{ regex: /rm\s+-rf\s+~/, message: 'Destructive operation on home directory' },
|
|
22
|
+
{ regex: /\bchmod\s+777/, message: 'Insecure permission change (chmod 777)' },
|
|
23
|
+
// Credential access
|
|
24
|
+
{ regex: /~\/\.ssh/, message: 'SSH credential access' },
|
|
25
|
+
{ regex: /~\/\.aws/, message: 'AWS credential access' },
|
|
26
|
+
{ regex: /~\/\.config\/gcloud/, message: 'GCloud credential access' },
|
|
27
|
+
{ regex: /~\/\.npmrc/, message: 'npm credential access' },
|
|
28
|
+
// Environment leakage (reading env vars beyond project)
|
|
29
|
+
{ regex: /process\.env\.(AWS_|GITHUB_TOKEN|NPM_TOKEN|SECRET)/, message: 'Sensitive environment variable access' },
|
|
30
|
+
// Directory traversal
|
|
31
|
+
{ regex: /\.\.\/(\.\.\/){2,}/, message: 'Deep directory traversal (potential escape)' },
|
|
32
|
+
];
|
|
33
|
+
/**
|
|
34
|
+
* Patterns that produce warnings (non-blocking).
|
|
35
|
+
*/
|
|
36
|
+
const WARNING_PATTERNS = [
|
|
37
|
+
{ regex: /process\.env\./, message: 'Environment variable access (verify scope is project-local)' },
|
|
38
|
+
{ regex: /\bfetch\s*\(/, message: 'Network request (verify URL is safe)' },
|
|
39
|
+
{ regex: /https?:\/\/[^\s"'`]+\.(sh|py|js|ts)\b/, message: 'URL to executable script' },
|
|
40
|
+
];
|
|
41
|
+
/**
|
|
42
|
+
* Size limits for skill files.
|
|
43
|
+
*/
|
|
44
|
+
const MAX_FILE_SIZE = 50 * 1024; // 50KB per file
|
|
45
|
+
const MAX_TOTAL_SIZE = 200 * 1024; // 200KB total
|
|
46
|
+
/**
|
|
47
|
+
* Validate a skill file's content for security issues.
|
|
48
|
+
*/
|
|
49
|
+
export function validateSecurity(content, filename) {
|
|
50
|
+
const findings = [];
|
|
51
|
+
const lines = content.split('\n');
|
|
52
|
+
// Size check
|
|
53
|
+
if (content.length > MAX_FILE_SIZE) {
|
|
54
|
+
findings.push({
|
|
55
|
+
pattern: 'size',
|
|
56
|
+
message: `File exceeds ${MAX_FILE_SIZE / 1024}KB limit (${Math.round(content.length / 1024)}KB)`,
|
|
57
|
+
line: 0,
|
|
58
|
+
severity: 'block',
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
// Pattern checks
|
|
62
|
+
for (let i = 0; i < lines.length; i++) {
|
|
63
|
+
const line = lines[i];
|
|
64
|
+
// Skip markdown code fence labels (```bash, ```yaml, etc.)
|
|
65
|
+
// but DO check content inside code blocks
|
|
66
|
+
if (/^```\w*$/.test(line.trim()))
|
|
67
|
+
continue;
|
|
68
|
+
for (const { regex, message } of BLOCKED_PATTERNS) {
|
|
69
|
+
if (regex.test(line)) {
|
|
70
|
+
findings.push({ pattern: regex.source, message, line: i + 1, severity: 'block' });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (const { regex, message } of WARNING_PATTERNS) {
|
|
74
|
+
if (regex.test(line)) {
|
|
75
|
+
findings.push({ pattern: regex.source, message, line: i + 1, severity: 'warn' });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const blocked = findings.some(f => f.severity === 'block');
|
|
80
|
+
return {
|
|
81
|
+
passed: !blocked,
|
|
82
|
+
findings,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Validate a collection of skill files for total size.
|
|
87
|
+
*/
|
|
88
|
+
export function validateTotalSize(files) {
|
|
89
|
+
const totalSize = files.reduce((sum, f) => sum + f.content.length, 0);
|
|
90
|
+
if (totalSize > MAX_TOTAL_SIZE) {
|
|
91
|
+
return {
|
|
92
|
+
pattern: 'total_size',
|
|
93
|
+
message: `Total size exceeds ${MAX_TOTAL_SIZE / 1024}KB limit (${Math.round(totalSize / 1024)}KB)`,
|
|
94
|
+
line: 0,
|
|
95
|
+
severity: 'block',
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/registry/security.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SKILL.md schema — defines the format for publishable skills.
|
|
3
|
+
*
|
|
4
|
+
* A SKILL.md file has YAML frontmatter with required and optional fields,
|
|
5
|
+
* followed by the skill content in markdown.
|
|
6
|
+
*/
|
|
7
|
+
export interface SkillManifest {
|
|
8
|
+
name: string;
|
|
9
|
+
version: string;
|
|
10
|
+
description: string;
|
|
11
|
+
author: string;
|
|
12
|
+
tags: string[];
|
|
13
|
+
stack: string;
|
|
14
|
+
tools: string[];
|
|
15
|
+
'allowed-tools'?: string;
|
|
16
|
+
'argument-hint'?: string;
|
|
17
|
+
license?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ParsedSkill {
|
|
20
|
+
manifest: SkillManifest;
|
|
21
|
+
content: string;
|
|
22
|
+
raw: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Parse a SKILL.md file into its manifest and content.
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseSkillFile(raw: string): ParsedSkill;
|
|
28
|
+
export declare class SkillValidationError extends Error {
|
|
29
|
+
constructor(message: string);
|
|
30
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SKILL.md schema — defines the format for publishable skills.
|
|
3
|
+
*
|
|
4
|
+
* A SKILL.md file has YAML frontmatter with required and optional fields,
|
|
5
|
+
* followed by the skill content in markdown.
|
|
6
|
+
*/
|
|
7
|
+
const REQUIRED_FIELDS = ['name', 'version', 'description', 'author'];
|
|
8
|
+
/**
|
|
9
|
+
* Parse a SKILL.md file into its manifest and content.
|
|
10
|
+
*/
|
|
11
|
+
export function parseSkillFile(raw) {
|
|
12
|
+
const frontmatterMatch = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
13
|
+
if (!frontmatterMatch) {
|
|
14
|
+
throw new SkillValidationError('SKILL.md must start with YAML frontmatter (--- ... ---)');
|
|
15
|
+
}
|
|
16
|
+
const [, yamlStr, content] = frontmatterMatch;
|
|
17
|
+
let manifest;
|
|
18
|
+
try {
|
|
19
|
+
// Simple YAML parser for frontmatter (key: value pairs)
|
|
20
|
+
manifest = parseSimpleYaml(yamlStr);
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
throw new SkillValidationError(`Invalid YAML frontmatter: ${e.message}`);
|
|
24
|
+
}
|
|
25
|
+
// Validate required fields
|
|
26
|
+
for (const field of REQUIRED_FIELDS) {
|
|
27
|
+
if (!manifest[field]) {
|
|
28
|
+
throw new SkillValidationError(`Missing required field: ${field}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Validate version is semver
|
|
32
|
+
if (!/^\d+\.\d+\.\d+$/.test(manifest.version)) {
|
|
33
|
+
throw new SkillValidationError(`Invalid version: "${manifest.version}" — must be semver (e.g., 1.0.0)`);
|
|
34
|
+
}
|
|
35
|
+
// Validate name is kebab-case
|
|
36
|
+
if (!/^[a-z][a-z0-9-]*$/.test(manifest.name)) {
|
|
37
|
+
throw new SkillValidationError(`Invalid name: "${manifest.name}" — must be lowercase kebab-case (e.g., "commit-review")`);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
manifest: {
|
|
41
|
+
name: manifest.name,
|
|
42
|
+
version: manifest.version,
|
|
43
|
+
description: manifest.description,
|
|
44
|
+
author: manifest.author,
|
|
45
|
+
tags: Array.isArray(manifest.tags) ? manifest.tags : [],
|
|
46
|
+
stack: manifest.stack ?? 'generic',
|
|
47
|
+
tools: Array.isArray(manifest.tools) ? manifest.tools : ['claude', 'cursor', 'codex'],
|
|
48
|
+
'allowed-tools': manifest['allowed-tools'],
|
|
49
|
+
'argument-hint': manifest['argument-hint'],
|
|
50
|
+
license: manifest.license,
|
|
51
|
+
},
|
|
52
|
+
content,
|
|
53
|
+
raw,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Simple YAML parser for frontmatter.
|
|
58
|
+
* Handles: key: value, key: [array], key: "quoted"
|
|
59
|
+
*/
|
|
60
|
+
function parseSimpleYaml(yaml) {
|
|
61
|
+
const result = {};
|
|
62
|
+
const lines = yaml.split('\n');
|
|
63
|
+
for (const line of lines) {
|
|
64
|
+
const trimmed = line.trim();
|
|
65
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
66
|
+
continue;
|
|
67
|
+
const colonIdx = trimmed.indexOf(':');
|
|
68
|
+
if (colonIdx === -1)
|
|
69
|
+
continue;
|
|
70
|
+
const key = trimmed.slice(0, colonIdx).trim();
|
|
71
|
+
let value = trimmed.slice(colonIdx + 1).trim();
|
|
72
|
+
// Array value: [a, b, c]
|
|
73
|
+
if (typeof value === 'string' && value.startsWith('[') && value.endsWith(']')) {
|
|
74
|
+
value = value.slice(1, -1).split(',').map(v => v.trim().replace(/^["']|["']$/g, '')).filter(Boolean);
|
|
75
|
+
}
|
|
76
|
+
// Quoted string
|
|
77
|
+
else if (typeof value === 'string' && ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")))) {
|
|
78
|
+
value = value.slice(1, -1);
|
|
79
|
+
}
|
|
80
|
+
// Boolean
|
|
81
|
+
else if (value === 'true')
|
|
82
|
+
value = true;
|
|
83
|
+
else if (value === 'false')
|
|
84
|
+
value = false;
|
|
85
|
+
result[key] = value;
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
export class SkillValidationError extends Error {
|
|
90
|
+
constructor(message) {
|
|
91
|
+
super(message);
|
|
92
|
+
this.name = 'SkillValidationError';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=skill-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-schema.js","sourceRoot":"","sources":["../../src/registry/skill-schema.ts"],"names":[],"mappings":"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"}
|
package/dist/ui/404.html
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><!--
|
|
1
|
+
<!DOCTYPE html><!--Z7X6LpFN441Kvx1ZYF2iY--><html lang="en" class="dark"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/721d4a8588775f36.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-4a462cecab786e93.js"/><script src="/_next/static/chunks/4bd1b696-c023c6e3521b1417.js" async=""></script><script src="/_next/static/chunks/255-54d3085ce94738a4.js" async=""></script><script src="/_next/static/chunks/main-app-c46afa2f48f3aaef.js" async=""></script><meta name="robots" content="noindex"/><title>404: This page could not be found.</title><title>Codeloop Board</title><meta name="description" content="Visual kanban board for codeloop"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body class="min-h-screen"><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-4a462cecab786e93.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[9766,[],\"\"]\n3:I[8924,[],\"\"]\n4:I[4431,[],\"OutletBoundary\"]\n6:I[5278,[],\"AsyncMetadataOutlet\"]\n8:I[4431,[],\"ViewportBoundary\"]\na:I[4431,[],\"MetadataBoundary\"]\nb:\"$Sreact.suspense\"\nd:I[7150,[],\"\"]\n:HL[\"/_next/static/css/721d4a8588775f36.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"Z7X6LpFN441Kvx1ZYF2iY\",\"p\":\"\",\"c\":[\"\",\"_not-found\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/721d4a8588775f36.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"dark\",\"children\":[\"$\",\"body\",null,{\"className\":\"min-h-screen\",\"children\":[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]}]]}],{\"children\":[\"/_not-found\",[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null,[\"$\",\"$L4\",null,{\"children\":[\"$L5\",[\"$\",\"$L6\",null,{\"promise\":\"$@7\"}]]}]]}],{},null,false]},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],[[\"$\",\"$L8\",null,{\"children\":\"$L9\"}],null],[\"$\",\"$La\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$b\",null,{\"fallback\":null,\"children\":\"$Lc\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$d\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"9:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n5:null\n"])</script><script>self.__next_f.push([1,"7:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"Codeloop Board\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Visual kanban board for codeloop\"}]],\"error\":null,\"digest\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"c:\"$7:metadata\"\n"])</script></body></html>
|
package/dist/ui/index.html
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><!--
|
|
1
|
+
<!DOCTYPE html><!--Z7X6LpFN441Kvx1ZYF2iY--><html lang="en" class="dark"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/721d4a8588775f36.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-4a462cecab786e93.js"/><script src="/_next/static/chunks/4bd1b696-c023c6e3521b1417.js" async=""></script><script src="/_next/static/chunks/255-54d3085ce94738a4.js" async=""></script><script src="/_next/static/chunks/main-app-c46afa2f48f3aaef.js" async=""></script><script src="/_next/static/chunks/423-bb541b7ae2733575.js" async=""></script><script src="/_next/static/chunks/app/page-a1867b0e8c871ff8.js" async=""></script><title>Codeloop Board</title><meta name="description" content="Visual kanban board for codeloop"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body class="min-h-screen"><div hidden=""><!--$--><!--/$--></div><div class="flex items-center justify-center h-screen"><div class="text-center"><div class="w-8 h-8 border-2 border-accent border-t-transparent rounded-full animate-spin mx-auto mb-4"></div><p class="text-sm text-muted">Connecting to board...</p></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-4a462cecab786e93.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[9766,[],\"\"]\n3:I[8924,[],\"\"]\n4:I[1959,[],\"ClientPageRoot\"]\n5:I[8453,[\"423\",\"static/chunks/423-bb541b7ae2733575.js\",\"974\",\"static/chunks/app/page-a1867b0e8c871ff8.js\"],\"default\"]\n8:I[4431,[],\"OutletBoundary\"]\na:I[5278,[],\"AsyncMetadataOutlet\"]\nc:I[4431,[],\"ViewportBoundary\"]\ne:I[4431,[],\"MetadataBoundary\"]\nf:\"$Sreact.suspense\"\n11:I[7150,[],\"\"]\n:HL[\"/_next/static/css/721d4a8588775f36.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"Z7X6LpFN441Kvx1ZYF2iY\",\"p\":\"\",\"c\":[\"\",\"\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/721d4a8588775f36.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"dark\",\"children\":[\"$\",\"body\",null,{\"className\":\"min-h-screen\",\"children\":[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"$L4\",null,{\"Component\":\"$5\",\"searchParams\":{},\"params\":{},\"promises\":[\"$@6\",\"$@7\"]}],null,[\"$\",\"$L8\",null,{\"children\":[\"$L9\",[\"$\",\"$La\",null,{\"promise\":\"$@b\"}]]}]]}],{},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[null,[[\"$\",\"$Lc\",null,{\"children\":\"$Ld\"}],null],[\"$\",\"$Le\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$f\",null,{\"fallback\":null,\"children\":\"$L10\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$11\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"6:{}\n7:\"$0:f:0:1:2:children:1:props:children:0:props:params\"\n"])</script><script>self.__next_f.push([1,"d:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n9:null\n"])</script><script>self.__next_f.push([1,"b:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"Codeloop Board\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Visual kanban board for codeloop\"}]],\"error\":null,\"digest\":\"$undefined\"}\n"])</script><script>self.__next_f.push([1,"10:\"$b:metadata\"\n"])</script></body></html>
|
package/dist/ui/index.txt
CHANGED
|
@@ -10,7 +10,7 @@ e:I[4431,[],"MetadataBoundary"]
|
|
|
10
10
|
f:"$Sreact.suspense"
|
|
11
11
|
11:I[7150,[],""]
|
|
12
12
|
:HL["/_next/static/css/721d4a8588775f36.css","style"]
|
|
13
|
-
0:{"P":null,"b":"
|
|
13
|
+
0:{"P":null,"b":"Z7X6LpFN441Kvx1ZYF2iY","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/721d4a8588775f36.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"dark","children":["$","body",null,{"className":"min-h-screen","children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$L4",null,{"Component":"$5","searchParams":{},"params":{},"promises":["$@6","$@7"]}],null,["$","$L8",null,{"children":["$L9",["$","$La",null,{"promise":"$@b"}]]}]]}],{},null,false]},null,false],["$","$1","h",{"children":[null,[["$","$Lc",null,{"children":"$Ld"}],null],["$","$Le",null,{"children":["$","div",null,{"hidden":true,"children":["$","$f",null,{"fallback":null,"children":"$L10"}]}]}]]}],false]],"m":"$undefined","G":["$11",[]],"s":false,"S":true}
|
|
14
14
|
6:{}
|
|
15
15
|
7:"$0:f:0:1:2:children:1:props:children:0:props:params"
|
|
16
16
|
d:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { tmpdir } from 'os';
|
|
5
|
+
import { loadWatchConfig } from '../index.js';
|
|
6
|
+
describe('loadWatchConfig', () => {
|
|
7
|
+
let tmpDir;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
tmpDir = mkdtempSync(join(tmpdir(), 'codeloop-watch-config-'));
|
|
10
|
+
});
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
13
|
+
});
|
|
14
|
+
it('returns defaults when no config exists', () => {
|
|
15
|
+
const config = loadWatchConfig(tmpDir);
|
|
16
|
+
expect(config.enabled).toBe(true);
|
|
17
|
+
expect(config.idle_timeout).toBe(300);
|
|
18
|
+
expect(config.signals.file_change).toBe(true);
|
|
19
|
+
expect(config.signals.git_commit).toBe(true);
|
|
20
|
+
expect(config.ignore).toContain('node_modules');
|
|
21
|
+
});
|
|
22
|
+
it('returns defaults when config has no watch section', () => {
|
|
23
|
+
mkdirSync(join(tmpDir, '.codeloop'), { recursive: true });
|
|
24
|
+
writeFileSync(join(tmpDir, '.codeloop/config.yaml'), 'project:\n name: test\n');
|
|
25
|
+
const config = loadWatchConfig(tmpDir);
|
|
26
|
+
expect(config.enabled).toBe(true);
|
|
27
|
+
expect(config.idle_timeout).toBe(300);
|
|
28
|
+
});
|
|
29
|
+
it('merges watch config with defaults', () => {
|
|
30
|
+
mkdirSync(join(tmpDir, '.codeloop'), { recursive: true });
|
|
31
|
+
writeFileSync(join(tmpDir, '.codeloop/config.yaml'), `watch:
|
|
32
|
+
enabled: false
|
|
33
|
+
idle_timeout: 600
|
|
34
|
+
signals:
|
|
35
|
+
file_change: false
|
|
36
|
+
ignore:
|
|
37
|
+
- vendor
|
|
38
|
+
`);
|
|
39
|
+
const config = loadWatchConfig(tmpDir);
|
|
40
|
+
expect(config.enabled).toBe(false);
|
|
41
|
+
expect(config.idle_timeout).toBe(600);
|
|
42
|
+
expect(config.signals.file_change).toBe(false);
|
|
43
|
+
expect(config.signals.git_commit).toBe(true); // default preserved
|
|
44
|
+
expect(config.ignore).toEqual(['vendor']);
|
|
45
|
+
});
|
|
46
|
+
it('handles malformed YAML gracefully', () => {
|
|
47
|
+
mkdirSync(join(tmpDir, '.codeloop'), { recursive: true });
|
|
48
|
+
writeFileSync(join(tmpDir, '.codeloop/config.yaml'), '{{not valid yaml');
|
|
49
|
+
const config = loadWatchConfig(tmpDir);
|
|
50
|
+
expect(config.enabled).toBe(true); // falls back to defaults
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
//# sourceMappingURL=config.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../../src/watch/__tests__/config.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { createIdleTimer } from '../signals.js';
|
|
3
|
+
describe('createIdleTimer', () => {
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
vi.useFakeTimers();
|
|
6
|
+
});
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
vi.useRealTimers();
|
|
9
|
+
});
|
|
10
|
+
it('fires after the specified timeout', () => {
|
|
11
|
+
const callback = vi.fn();
|
|
12
|
+
const timer = createIdleTimer(5000, callback);
|
|
13
|
+
vi.advanceTimersByTime(4999);
|
|
14
|
+
expect(callback).not.toHaveBeenCalled();
|
|
15
|
+
vi.advanceTimersByTime(1);
|
|
16
|
+
expect(callback).toHaveBeenCalledOnce();
|
|
17
|
+
expect(callback.mock.calls[0][0].type).toBe('idle');
|
|
18
|
+
expect(callback.mock.calls[0][0].data.idleSeconds).toBe(5);
|
|
19
|
+
timer.close();
|
|
20
|
+
});
|
|
21
|
+
it('resets the timer on reset()', () => {
|
|
22
|
+
const callback = vi.fn();
|
|
23
|
+
const timer = createIdleTimer(5000, callback);
|
|
24
|
+
vi.advanceTimersByTime(4000);
|
|
25
|
+
timer.reset();
|
|
26
|
+
vi.advanceTimersByTime(4000);
|
|
27
|
+
expect(callback).not.toHaveBeenCalled();
|
|
28
|
+
vi.advanceTimersByTime(1000);
|
|
29
|
+
expect(callback).toHaveBeenCalledOnce();
|
|
30
|
+
timer.close();
|
|
31
|
+
});
|
|
32
|
+
it('does not fire after close()', () => {
|
|
33
|
+
const callback = vi.fn();
|
|
34
|
+
const timer = createIdleTimer(5000, callback);
|
|
35
|
+
vi.advanceTimersByTime(3000);
|
|
36
|
+
timer.close();
|
|
37
|
+
vi.advanceTimersByTime(10000);
|
|
38
|
+
expect(callback).not.toHaveBeenCalled();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
//# sourceMappingURL=signals.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signals.test.js","sourceRoot":"","sources":["../../../src/watch/__tests__/signals.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAE9C,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAExC,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;QAC1B,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,EAAE,CAAC;QACxC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAE3D,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAE9C,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC7B,KAAK,CAAC,KAAK,EAAE,CAAC;QAEd,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAExC,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,EAAE,CAAC;QAExC,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAE9C,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC7B,KAAK,CAAC,KAAK,EAAE,CAAC;QAEd,EAAE,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|