@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.
Files changed (119) hide show
  1. package/README.md +133 -71
  2. package/dist/commands/install.d.ts +2 -0
  3. package/dist/commands/install.js +125 -0
  4. package/dist/commands/install.js.map +1 -0
  5. package/dist/commands/list.d.ts +2 -0
  6. package/dist/commands/list.js +35 -0
  7. package/dist/commands/list.js.map +1 -0
  8. package/dist/commands/login.d.ts +2 -0
  9. package/dist/commands/login.js +77 -0
  10. package/dist/commands/login.js.map +1 -0
  11. package/dist/commands/publish.d.ts +2 -0
  12. package/dist/commands/publish.js +125 -0
  13. package/dist/commands/publish.js.map +1 -0
  14. package/dist/commands/remove.d.ts +2 -0
  15. package/dist/commands/remove.js +31 -0
  16. package/dist/commands/remove.js.map +1 -0
  17. package/dist/commands/search.d.ts +2 -0
  18. package/dist/commands/search.js +85 -0
  19. package/dist/commands/search.js.map +1 -0
  20. package/dist/commands/status.d.ts +1 -0
  21. package/dist/commands/status.js +85 -11
  22. package/dist/commands/status.js.map +1 -1
  23. package/dist/commands/update.js +10 -22
  24. package/dist/commands/update.js.map +1 -1
  25. package/dist/commands/watch.d.ts +6 -0
  26. package/dist/commands/watch.js +111 -0
  27. package/dist/commands/watch.js.map +1 -0
  28. package/dist/index.js +17 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/lib/__tests__/scaffold.test.js +24 -0
  31. package/dist/lib/__tests__/scaffold.test.js.map +1 -1
  32. package/dist/lib/__tests__/smoke.test.js +41 -3
  33. package/dist/lib/__tests__/smoke.test.js.map +1 -1
  34. package/dist/lib/__tests__/validate-config.test.d.ts +1 -0
  35. package/dist/lib/__tests__/validate-config.test.js +109 -0
  36. package/dist/lib/__tests__/validate-config.test.js.map +1 -0
  37. package/dist/lib/scaffold.js +32 -3
  38. package/dist/lib/scaffold.js.map +1 -1
  39. package/dist/registry/__tests__/installer.test.d.ts +1 -0
  40. package/dist/registry/__tests__/installer.test.js +122 -0
  41. package/dist/registry/__tests__/installer.test.js.map +1 -0
  42. package/dist/registry/__tests__/local-index.test.d.ts +1 -0
  43. package/dist/registry/__tests__/local-index.test.js +73 -0
  44. package/dist/registry/__tests__/local-index.test.js.map +1 -0
  45. package/dist/registry/__tests__/lockfile.test.d.ts +1 -0
  46. package/dist/registry/__tests__/lockfile.test.js +93 -0
  47. package/dist/registry/__tests__/lockfile.test.js.map +1 -0
  48. package/dist/registry/__tests__/security.test.d.ts +1 -0
  49. package/dist/registry/__tests__/security.test.js +100 -0
  50. package/dist/registry/__tests__/security.test.js.map +1 -0
  51. package/dist/registry/__tests__/skill-schema.test.d.ts +1 -0
  52. package/dist/registry/__tests__/skill-schema.test.js +102 -0
  53. package/dist/registry/__tests__/skill-schema.test.js.map +1 -0
  54. package/dist/registry/index.d.ts +7 -0
  55. package/dist/registry/index.js +8 -0
  56. package/dist/registry/index.js.map +1 -0
  57. package/dist/registry/installer.d.ts +30 -0
  58. package/dist/registry/installer.js +133 -0
  59. package/dist/registry/installer.js.map +1 -0
  60. package/dist/registry/local-index.d.ts +32 -0
  61. package/dist/registry/local-index.js +58 -0
  62. package/dist/registry/local-index.js.map +1 -0
  63. package/dist/registry/lockfile.d.ts +40 -0
  64. package/dist/registry/lockfile.js +85 -0
  65. package/dist/registry/lockfile.js.map +1 -0
  66. package/dist/registry/security.d.ts +25 -0
  67. package/dist/registry/security.js +100 -0
  68. package/dist/registry/security.js.map +1 -0
  69. package/dist/registry/skill-schema.d.ts +30 -0
  70. package/dist/registry/skill-schema.js +95 -0
  71. package/dist/registry/skill-schema.js.map +1 -0
  72. package/dist/ui/404.html +1 -1
  73. package/dist/ui/index.html +1 -1
  74. package/dist/ui/index.txt +1 -1
  75. package/dist/watch/__tests__/config.test.d.ts +1 -0
  76. package/dist/watch/__tests__/config.test.js +53 -0
  77. package/dist/watch/__tests__/config.test.js.map +1 -0
  78. package/dist/watch/__tests__/signals.test.d.ts +1 -0
  79. package/dist/watch/__tests__/signals.test.js +41 -0
  80. package/dist/watch/__tests__/signals.test.js.map +1 -0
  81. package/dist/watch/__tests__/triggers.test.d.ts +1 -0
  82. package/dist/watch/__tests__/triggers.test.js +92 -0
  83. package/dist/watch/__tests__/triggers.test.js.map +1 -0
  84. package/dist/watch/index.d.ts +21 -0
  85. package/dist/watch/index.js +88 -0
  86. package/dist/watch/index.js.map +1 -0
  87. package/dist/watch/reporter.d.ts +11 -0
  88. package/dist/watch/reporter.js +44 -0
  89. package/dist/watch/reporter.js.map +1 -0
  90. package/dist/watch/signals.d.ts +38 -0
  91. package/dist/watch/signals.js +119 -0
  92. package/dist/watch/signals.js.map +1 -0
  93. package/dist/watch/triggers.d.ts +10 -0
  94. package/dist/watch/triggers.js +67 -0
  95. package/dist/watch/triggers.js.map +1 -0
  96. package/package.json +3 -2
  97. package/registry/index.json +106 -0
  98. package/starters/generic.yaml +37 -0
  99. package/starters/go.yaml +39 -0
  100. package/starters/node-typescript.yaml +39 -0
  101. package/starters/python.yaml +42 -0
  102. package/templates/commands/debug.md +142 -0
  103. package/templates/commands/deploy.md +144 -0
  104. package/templates/commands/design.md +102 -0
  105. package/templates/commands/manage.md +1 -1
  106. package/templates/commands/plan.md +4 -3
  107. package/templates/commands/qa.md +155 -0
  108. package/templates/commands/ship.md +187 -0
  109. package/templates/commands/test.md +133 -0
  110. package/templates/seeds/go-gotchas.md +28 -0
  111. package/templates/seeds/go-patterns.md +22 -0
  112. package/templates/seeds/node-typescript-gotchas.md +30 -0
  113. package/templates/seeds/node-typescript-patterns.md +27 -0
  114. package/templates/seeds/python-gotchas.md +30 -0
  115. package/templates/seeds/python-patterns.md +19 -0
  116. package/templates/seeds/universal-gotchas.md +11 -0
  117. package/templates/seeds/universal-patterns.md +11 -0
  118. /package/dist/ui/_next/static/{uiiPJR68HihKQsXtPj0fm → Z7X6LpFN441Kvx1ZYF2iY}/_buildManifest.js +0 -0
  119. /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><!--uiiPJR68HihKQsXtPj0fm--><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,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;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\":\"uiiPJR68HihKQsXtPj0fm\",\"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>
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,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;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>
@@ -1 +1 @@
1
- <!DOCTYPE html><!--uiiPJR68HihKQsXtPj0fm--><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\":\"uiiPJR68HihKQsXtPj0fm\",\"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>
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":"uiiPJR68HihKQsXtPj0fm","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}
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 {};