sinapse-ai 7.7.3 → 7.7.5

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 (55) hide show
  1. package/.codex/catalog.json +157 -0
  2. package/.codex/command-registry.json +441 -0
  3. package/.codex/delegation-matrix.json +512 -0
  4. package/.codex/handoff-packet.schema.json +148 -0
  5. package/.codex/scripts/generate-codex-greeting.js +101 -0
  6. package/.codex/scripts/resolve-codex-command.js +147 -0
  7. package/.codex/scripts/resolve-codex-delegation.js +205 -0
  8. package/.codex/skills/sinapse-analyst/SKILL.md +5 -4
  9. package/.codex/skills/sinapse-architect/SKILL.md +5 -4
  10. package/.codex/skills/sinapse-data-engineer/SKILL.md +5 -4
  11. package/.codex/skills/sinapse-dev/SKILL.md +5 -4
  12. package/.codex/skills/sinapse-devops/SKILL.md +5 -4
  13. package/.codex/skills/sinapse-orqx/SKILL.md +10 -15
  14. package/.codex/skills/sinapse-pm/SKILL.md +5 -4
  15. package/.codex/skills/sinapse-po/SKILL.md +4 -3
  16. package/.codex/skills/sinapse-qa/SKILL.md +12 -11
  17. package/.codex/skills/sinapse-sm/SKILL.md +5 -4
  18. package/.codex/skills/sinapse-squad-creator/SKILL.md +5 -4
  19. package/.codex/skills/sinapse-ux-design-expert/SKILL.md +5 -4
  20. package/.codex/tasks/convene-sinapse-council.md +28 -0
  21. package/.codex/tasks/create-sinapse-strategic-brief.md +29 -0
  22. package/.codex/tasks/onboard-sinapse-codex.md +34 -0
  23. package/.codex/tasks/plan-sinapse-initiative.md +33 -0
  24. package/.codex/tasks/resolve-sinapse-conflict.md +28 -0
  25. package/.codex/tasks/route-sinapse-request.md +35 -0
  26. package/.codex/tasks/status-sinapse-capabilities.md +28 -0
  27. package/.sinapse-ai/core-config.yaml +1 -1
  28. package/.sinapse-ai/data/entity-registry.yaml +874 -749
  29. package/.sinapse-ai/data/registry-update-log.jsonl +13 -0
  30. package/.sinapse-ai/infrastructure/scripts/codex-parity/catalog.js +123 -0
  31. package/.sinapse-ai/infrastructure/scripts/codex-skills-sync/index.js +60 -11
  32. package/.sinapse-ai/infrastructure/scripts/codex-skills-sync/validate.js +44 -16
  33. package/.sinapse-ai/infrastructure/scripts/sync-codex-local-first.js +156 -0
  34. package/.sinapse-ai/infrastructure/scripts/validate-codex-command-registry.js +264 -0
  35. package/.sinapse-ai/infrastructure/scripts/validate-codex-delegation.js +292 -0
  36. package/.sinapse-ai/infrastructure/scripts/validate-codex-integration.js +15 -6
  37. package/.sinapse-ai/infrastructure/scripts/validate-codex-sync.js +159 -0
  38. package/.sinapse-ai/infrastructure/scripts/validate-parity.js +3 -1
  39. package/.sinapse-ai/infrastructure/scripts/validate-paths.js +8 -10
  40. package/.sinapse-ai/infrastructure/templates/safe-collab/README.md +8 -0
  41. package/.sinapse-ai/install-manifest.yaml +39 -19
  42. package/.sinapse-ai/project-config.yaml +1 -1
  43. package/bin/utils/collab-start.js +267 -0
  44. package/bin/utils/git-branch-guard.js +76 -0
  45. package/bin/utils/pre-push-safety.js +110 -0
  46. package/bin/utils/staged-secret-scan.js +108 -0
  47. package/docs/codex-parity-program.md +670 -0
  48. package/docs/codex-total-parity-orchestration-plan.md +301 -0
  49. package/docs/codex-workflow-task-parity.md +87 -0
  50. package/docs/collaboration-autonomy-plan.md +243 -0
  51. package/docs/guides/framework-contributor-mode.md +310 -0
  52. package/docs/guides/parallel-collaboration-source-of-truth.md +481 -0
  53. package/package.json +14 -3
  54. package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +7 -2
  55. package/packages/installer/tests/unit/entity-registry-bootstrap.test.js +2 -2
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const { spawnSync } = require('child_process');
7
+ const { loadCodexCatalogConfig } = require('./codex-parity/catalog');
8
+ const { validateCodexCommandRegistry } = require('./validate-codex-command-registry');
9
+ const { validateCodexDelegation } = require('./validate-codex-delegation');
10
+ const { validateCodexIntegration } = require('./validate-codex-integration');
11
+ const { validateCodexSkills } = require('./codex-skills-sync/validate');
12
+ const { validatePaths } = require('./validate-paths');
13
+
14
+ function parseArgs(argv = process.argv.slice(2)) {
15
+ const args = new Set(argv);
16
+ return {
17
+ quiet: args.has('--quiet') || args.has('-q'),
18
+ json: args.has('--json'),
19
+ };
20
+ }
21
+
22
+ function runLegacyCodexValidate(projectRoot, options = {}) {
23
+ const args = [
24
+ path.join('.sinapse-ai', 'infrastructure', 'scripts', 'ide-sync', 'index.js'),
25
+ 'validate',
26
+ '--ide',
27
+ 'codex',
28
+ '--strict',
29
+ ];
30
+
31
+ if (options.quiet) args.push('--quiet');
32
+
33
+ const result = spawnSync(process.execPath, args, {
34
+ cwd: projectRoot,
35
+ encoding: 'utf8',
36
+ });
37
+
38
+ return {
39
+ ok: result.status === 0,
40
+ mode: 'canonical',
41
+ checks: [],
42
+ errors: result.status === 0 ? [] : ['Legacy Codex sync validation failed'],
43
+ warnings: [],
44
+ raw: [result.stdout, result.stderr].filter(Boolean).join('\n').trim(),
45
+ };
46
+ }
47
+
48
+ function normalizeCheck(id, input) {
49
+ return {
50
+ id,
51
+ ok: Boolean(input?.ok),
52
+ errors: Array.isArray(input?.errors) ? input.errors : [],
53
+ warnings: Array.isArray(input?.warnings) ? input.warnings : [],
54
+ metrics: input?.metrics || {},
55
+ };
56
+ }
57
+
58
+ function validateCodexSync(options = {}, deps = {}) {
59
+ const projectRoot = options.projectRoot || process.cwd();
60
+ const config = loadCodexCatalogConfig(projectRoot);
61
+ const runLegacyValidate = deps.runLegacyCodexValidate || runLegacyCodexValidate;
62
+ const runCodexCommands = deps.validateCodexCommandRegistry || validateCodexCommandRegistry;
63
+ const runCodexDelegation = deps.validateCodexDelegation || validateCodexDelegation;
64
+ const runCodexIntegration = deps.validateCodexIntegration || validateCodexIntegration;
65
+ const runCodexSkills = deps.validateCodexSkills || validateCodexSkills;
66
+ const runPaths = deps.validatePaths || validatePaths;
67
+
68
+ if (config.catalogMode !== 'expanded') {
69
+ return runLegacyValidate(projectRoot, options);
70
+ }
71
+
72
+ const catalogPath = path.join(projectRoot, '.codex', 'catalog.json');
73
+ const checks = [
74
+ normalizeCheck('codex-integration', runCodexIntegration({ projectRoot, quiet: true })),
75
+ normalizeCheck('codex-commands', runCodexCommands({ projectRoot, quiet: true })),
76
+ normalizeCheck('codex-delegation', runCodexDelegation({ projectRoot, quiet: true })),
77
+ normalizeCheck('codex-skills', runCodexSkills({ projectRoot, strict: true, quiet: true })),
78
+ normalizeCheck('paths', runPaths({ projectRoot, quiet: true })),
79
+ ];
80
+
81
+ if (!fs.existsSync(catalogPath)) {
82
+ checks.unshift({
83
+ id: 'codex-catalog',
84
+ ok: false,
85
+ errors: ['Missing Codex catalog file: .codex/catalog.json'],
86
+ warnings: [],
87
+ metrics: {},
88
+ });
89
+ } else {
90
+ checks.unshift({
91
+ id: 'codex-catalog',
92
+ ok: true,
93
+ errors: [],
94
+ warnings: [],
95
+ metrics: {},
96
+ });
97
+ }
98
+
99
+ return {
100
+ ok: checks.every((check) => check.ok),
101
+ mode: 'expanded',
102
+ checks,
103
+ errors: checks.flatMap((check) => check.errors.map((error) => `${check.id}: ${error}`)),
104
+ warnings: checks.flatMap((check) => check.warnings.map((warning) => `${check.id}: ${warning}`)),
105
+ };
106
+ }
107
+
108
+ function formatHumanReport(result) {
109
+ if (result.mode === 'canonical' && result.raw) {
110
+ return result.raw;
111
+ }
112
+
113
+ const lines = [
114
+ result.ok
115
+ ? 'OK Codex sync validation passed'
116
+ : `X Codex sync validation failed (${result.errors.length} issue(s))`,
117
+ ];
118
+
119
+ for (const check of result.checks || []) {
120
+ lines.push(`${check.ok ? 'OK' : 'X'} ${check.id}`);
121
+ if (check.errors.length > 0) {
122
+ lines.push(...check.errors.map((error) => `- ${error}`));
123
+ }
124
+ if (check.warnings.length > 0) {
125
+ lines.push(...check.warnings.map((warning) => `! ${warning}`));
126
+ }
127
+ }
128
+
129
+ return lines.join('\n');
130
+ }
131
+
132
+ function main() {
133
+ const args = parseArgs();
134
+ const result = validateCodexSync(args);
135
+
136
+ if (!args.quiet) {
137
+ if (args.json) {
138
+ console.log(JSON.stringify(result, null, 2));
139
+ } else {
140
+ console.log(formatHumanReport(result));
141
+ }
142
+ }
143
+
144
+ if (!result.ok) {
145
+ process.exitCode = 1;
146
+ }
147
+ }
148
+
149
+ if (require.main === module) {
150
+ main();
151
+ }
152
+
153
+ module.exports = {
154
+ parseArgs,
155
+ validateCodexSync,
156
+ formatHumanReport,
157
+ runLegacyCodexValidate,
158
+ normalizeCheck,
159
+ };
@@ -6,6 +6,7 @@ const path = require('path');
6
6
  const yaml = require('js-yaml');
7
7
  const { spawnSync } = require('child_process');
8
8
  const { validateClaudeIntegration } = require('./validate-claude-integration');
9
+ const { validateCodexSync } = require('./validate-codex-sync');
9
10
  const { validateCodexIntegration } = require('./validate-codex-integration');
10
11
  const { validateCodexSkills } = require('./codex-skills-sync/validate');
11
12
  const { validatePaths } = require('./validate-paths');
@@ -218,6 +219,7 @@ function diffCompatibilityContracts(currentContract, previousContract) {
218
219
  function runParityValidation(options = {}, deps = {}) {
219
220
  const projectRoot = options.projectRoot || process.cwd();
220
221
  const runSync = deps.runSyncValidate || runSyncValidate;
222
+ const runCodexSync = deps.validateCodexSync || validateCodexSync;
221
223
  const runClaudeIntegration = deps.validateClaudeIntegration || validateClaudeIntegration;
222
224
  const runCodexIntegration = deps.validateCodexIntegration || validateCodexIntegration;
223
225
  const runCodexSkills = deps.validateCodexSkills || validateCodexSkills;
@@ -234,7 +236,7 @@ function runParityValidation(options = {}, deps = {}) {
234
236
  const checks = [
235
237
  { id: 'claude-sync', exec: () => runSync('claude-code', projectRoot) },
236
238
  { id: 'claude-integration', exec: () => runClaudeIntegration({ projectRoot }) },
237
- { id: 'codex-sync', exec: () => runSync('codex', projectRoot) },
239
+ { id: 'codex-sync', exec: () => runCodexSync({ projectRoot, quiet: true }) },
238
240
  { id: 'codex-integration', exec: () => runCodexIntegration({ projectRoot }) },
239
241
  { id: 'codex-skills', exec: () => runCodexSkills({ projectRoot, strict: true, quiet: true }) },
240
242
  { id: 'paths', exec: () => runPaths({ projectRoot }) },
@@ -3,6 +3,10 @@
3
3
 
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
+ const {
7
+ loadCodexCatalogConfig,
8
+ validateSkillActivationPaths,
9
+ } = require('./codex-parity/catalog');
6
10
 
7
11
  const FORBIDDEN_ABSOLUTE_PATTERNS = [
8
12
  /\/Users\/[^\s/'"]+/g,
@@ -54,21 +58,15 @@ function collectAbsolutePathViolations(content, filePath) {
54
58
  return errors;
55
59
  }
56
60
 
57
- function validateSkillPathConventions(content, filePath) {
58
- const errors = [];
59
- if (!content.includes('.sinapse-ai/development/agents/')) {
60
- errors.push(`${filePath} missing canonical source path ".sinapse-ai/development/agents/"`);
61
- }
62
- if (!content.includes('.sinapse-ai/development/scripts/generate-greeting.js')) {
63
- errors.push(`${filePath} missing canonical greeting script path`);
64
- }
65
- return errors;
61
+ function validateSkillPathConventions(content, filePath, config) {
62
+ return validateSkillActivationPaths(content, filePath, config);
66
63
  }
67
64
 
68
65
  function validatePaths(options = {}) {
69
66
  const resolved = { ...getDefaultOptions(), ...options };
70
67
  const errors = [];
71
68
  const checkedFiles = [];
69
+ const config = loadCodexCatalogConfig(resolved.projectRoot);
72
70
 
73
71
  const skillFiles = listSkillFiles(resolved.skillsDir);
74
72
  const filesToCheck = [...resolved.requiredFiles, ...skillFiles];
@@ -90,7 +88,7 @@ function validatePaths(options = {}) {
90
88
  errors.push(...collectAbsolutePathViolations(content, path.relative(resolved.projectRoot, file)));
91
89
 
92
90
  if (file.endsWith('SKILL.md')) {
93
- errors.push(...validateSkillPathConventions(content, path.relative(resolved.projectRoot, file)));
91
+ errors.push(...validateSkillPathConventions(content, path.relative(resolved.projectRoot, file), config));
94
92
  }
95
93
  }
96
94
 
@@ -2,6 +2,14 @@
2
2
 
3
3
  Template reutilizavel para configurar colaboracao segura em qualquer projeto.
4
4
 
5
+ ## Fonte canonica
6
+
7
+ Para a politica completa e atualizada de colaboracao paralela, use:
8
+
9
+ - `docs/guides/parallel-collaboration-source-of-truth.md`
10
+
11
+ Este README explica o template. O documento acima define a regra mestra.
12
+
5
13
  ## Uso rapido
6
14
 
7
15
  ```bash
@@ -7,10 +7,10 @@
7
7
  # - SHA256 hashes for change detection
8
8
  # - File types for categorization
9
9
  #
10
- version: 7.7.3
11
- generated_at: "2026-04-02T01:44:04.986Z"
10
+ version: 7.7.5
11
+ generated_at: "2026-04-03T00:28:11.575Z"
12
12
  generator: scripts/generate-install-manifest.js
13
- file_count: 1113
13
+ file_count: 1118
14
14
  files:
15
15
  - path: cli/commands/config/index.js
16
16
  hash: sha256:66f111eceef0f60fa0a8904add783b615d55b01d5fe36408623c3dd828e702f6
@@ -189,9 +189,9 @@ files:
189
189
  type: cli
190
190
  size: 5907
191
191
  - path: core-config.yaml
192
- hash: sha256:1dfc00cfc993bacb17f683aacdf898b856744a33a76183adab2d1c1896be8b31
192
+ hash: sha256:fc24a8286b20ec2558c90186e99124b96d26b287ff107b747c2b68b1102e0cc8
193
193
  type: config
194
- size: 10514
194
+ size: 10513
195
195
  - path: core/code-intel/code-intel-client.js
196
196
  hash: sha256:6c9a08a37775acf90397aa079a4ad2c5edcc47f2cfd592b26ae9f3d154d1deb8
197
197
  type: core
@@ -1237,9 +1237,9 @@ files:
1237
1237
  type: data
1238
1238
  size: 9586
1239
1239
  - path: data/entity-registry.yaml
1240
- hash: sha256:17d002e1c7b1dc347bc369fd82b28a931babcb69f16efc9ac76eb484eaacc1a1
1240
+ hash: sha256:e3e57f3070134b76851762a0666b665228f061d3ff37ee513e7b6ae5dcef3b62
1241
1241
  type: data
1242
- size: 511993
1242
+ size: 515807
1243
1243
  - path: data/learned-patterns.yaml
1244
1244
  hash: sha256:24ac0b160615583a0ff783d3da8af80b7f94191575d6db2054ec8e10a3f945dc
1245
1245
  type: data
@@ -3052,14 +3052,18 @@ files:
3052
3052
  hash: sha256:47b3f9044ce9b92b8c486a75da82afc4cc6b49d5d97e722ef5a1938fbf49943a
3053
3053
  type: script
3054
3054
  size: 40744
3055
+ - path: infrastructure/scripts/codex-parity/catalog.js
3056
+ hash: sha256:cfef97860788a119c44cbb18b4249ad538814f99a03b2ca13e66dd74aa8b5242
3057
+ type: script
3058
+ size: 3527
3055
3059
  - path: infrastructure/scripts/codex-skills-sync/index.js
3056
- hash: sha256:cea7c65175ebf5f1f7d47d0dc536e5889e420e6c137a41acd522be8b69fe6327
3060
+ hash: sha256:9eb2637a4f1f6edeabf358734b14e4c709bed37560f2e40aebf1ea3ef4572c3c
3057
3061
  type: script
3058
- size: 5268
3062
+ size: 7494
3059
3063
  - path: infrastructure/scripts/codex-skills-sync/validate.js
3060
- hash: sha256:07c2d17438890bc5bd3f2cc208692ff65f6d1f394390066ff9869e03973c7e2b
3064
+ hash: sha256:1a1301d2e3f558bedf3a1ab2c5c9ee2868a4c253068311d5014987917f367443
3061
3065
  type: script
3062
- size: 4577
3066
+ size: 5626
3063
3067
  - path: infrastructure/scripts/collect-tool-usage.js
3064
3068
  hash: sha256:510b621078602273fb146eef57d115b08988ea43d03eb5d92192050c13e95efe
3065
3069
  type: script
@@ -3372,6 +3376,10 @@ files:
3372
3376
  hash: sha256:ceb0450fa12fa48f0255bb4565858eb1a97b28c30b98d36cb61d52d72e08b054
3373
3377
  type: script
3374
3378
  size: 22394
3379
+ - path: infrastructure/scripts/sync-codex-local-first.js
3380
+ hash: sha256:ad5245ead7243a39cd5116f44fcf1234ad54b016e365dfefa017764a76f4f3eb
3381
+ type: script
3382
+ size: 4089
3375
3383
  - path: infrastructure/scripts/template-engine.js
3376
3384
  hash: sha256:de1bc8ce51eeabf6f6ec558d35107fbc4843ac76e3361044ba9f47bb6af4f058
3377
3385
  type: script
@@ -3420,22 +3428,34 @@ files:
3420
3428
  hash: sha256:4447fbf8cffdf20b34fad71eb4caf5aec0726f2b8f27cd49cd1bd59d69cdc009
3421
3429
  type: script
3422
3430
  size: 2838
3431
+ - path: infrastructure/scripts/validate-codex-command-registry.js
3432
+ hash: sha256:a37a4c5fc8a4c3a7ce7ba240aeea16abc0a75871751f66eeb84991604ff066bf
3433
+ type: script
3434
+ size: 7266
3435
+ - path: infrastructure/scripts/validate-codex-delegation.js
3436
+ hash: sha256:a71224ad31b3a28bcc26953f75bbd6387686b3b906404c833f52b8dfff306384
3437
+ type: script
3438
+ size: 9532
3423
3439
  - path: infrastructure/scripts/validate-codex-integration.js
3424
- hash: sha256:01065d27f1c9e002b3985f1b5090cf123369fcf528012c2ec8c4a02df9b3645e
3440
+ hash: sha256:c553cc493ddbfecf41b865f8a1075df56b6500c3b2b315bae8b7e7bcec4db85a
3441
+ type: script
3442
+ size: 4742
3443
+ - path: infrastructure/scripts/validate-codex-sync.js
3444
+ hash: sha256:31bcfc0cdf307e7c14064eaf0ddbf99a14344c21306937c9a7d9cc7ebba260f8
3425
3445
  type: script
3426
- size: 4127
3446
+ size: 4679
3427
3447
  - path: infrastructure/scripts/validate-output-pattern.js
3428
3448
  hash: sha256:91111d656e8d7b38a20a1bda753e663b74318f75cdab2025c7e0b84c775fc83d
3429
3449
  type: script
3430
3450
  size: 6692
3431
3451
  - path: infrastructure/scripts/validate-parity.js
3432
- hash: sha256:5cbd1c4458fb3e485872635e2210b3d3406c22381185039ef112db8912887201
3452
+ hash: sha256:284c7715cc65b76579284024bbe5176b9230340d841dbfe79559c8f5844109cb
3433
3453
  type: script
3434
- size: 12121
3454
+ size: 12266
3435
3455
  - path: infrastructure/scripts/validate-paths.js
3436
- hash: sha256:f657c32be91ed63e0b82d5b134b143d03174622eba60fb4b556adc570b8c7f5a
3456
+ hash: sha256:942f6de9e621b7331f61dc915db5d24b987b67d1ae3377882330f7013dd214e9
3437
3457
  type: script
3438
- size: 3779
3458
+ size: 3671
3439
3459
  - path: infrastructure/scripts/validate-user-profile.js
3440
3460
  hash: sha256:0f78a5aebd85b4b0d2a5b0f2d81ccbbbe0f2d25ed1cfcafeb129734852217683
3441
3461
  type: script
@@ -3525,9 +3545,9 @@ files:
3525
3545
  type: template
3526
3546
  size: 338
3527
3547
  - path: infrastructure/templates/safe-collab/README.md
3528
- hash: sha256:53e1193eaf30b5a4d9a5d2c5f700b22a210515abb661baf2b5ebe056343806fe
3548
+ hash: sha256:0b4f3d0ce9b7111d812917312ae504df976245d902eecfa501f73e7aa0719813
3529
3549
  type: template
3530
- size: 1958
3550
+ size: 2179
3531
3551
  - path: infrastructure/templates/safe-collab/safe-collaboration-rule.md
3532
3552
  hash: sha256:9631681340fddc314080f9182b76ccfbb1ee3441d92ce181067e76cace960a5e
3533
3553
  type: template
@@ -70,7 +70,7 @@ github_integration:
70
70
  "style/": "style"
71
71
  "build/": "build"
72
72
  default_type: "feat"
73
- auto_assign_reviewers: false
73
+ auto_assign_reviewers: true
74
74
  draft_by_default: false
75
75
  semantic_release:
76
76
  enabled: true
@@ -0,0 +1,267 @@
1
+ 'use strict';
2
+
3
+ const { execFileSync, execSync } = require('child_process');
4
+ const path = require('path');
5
+
6
+ function execGit(command) {
7
+ return execSync(command, {
8
+ encoding: 'utf8',
9
+ stdio: ['ignore', 'pipe', 'pipe'],
10
+ }).trim();
11
+ }
12
+
13
+ function safeExec(command) {
14
+ try {
15
+ return execGit(command);
16
+ } catch {
17
+ return '';
18
+ }
19
+ }
20
+
21
+ function parseArgs(argv) {
22
+ const parsed = {
23
+ adoptCurrent: false,
24
+ check: false,
25
+ type: 'feat',
26
+ owner: null,
27
+ storyId: null,
28
+ slug: null,
29
+ };
30
+
31
+ for (const arg of argv) {
32
+ if (arg === '--adopt-current') {
33
+ parsed.adoptCurrent = true;
34
+ continue;
35
+ }
36
+ if (arg === '--check') {
37
+ parsed.check = true;
38
+ continue;
39
+ }
40
+ if (arg.startsWith('--type=')) {
41
+ parsed.type = arg.split('=')[1] || parsed.type;
42
+ continue;
43
+ }
44
+ if (arg.startsWith('--owner=')) {
45
+ parsed.owner = arg.split('=')[1] || null;
46
+ continue;
47
+ }
48
+ if (!parsed.storyId) {
49
+ parsed.storyId = arg;
50
+ continue;
51
+ }
52
+ if (!parsed.slug) {
53
+ parsed.slug = arg;
54
+ }
55
+ }
56
+
57
+ return parsed;
58
+ }
59
+
60
+ function resolveDefaultBranch() {
61
+ try {
62
+ const remoteHead = execGit('git symbolic-ref --short refs/remotes/origin/HEAD');
63
+ return remoteHead.replace(/^origin\//, '');
64
+ } catch {
65
+ return 'main';
66
+ }
67
+ }
68
+
69
+ function getCurrentBranch() {
70
+ return execGit('git rev-parse --abbrev-ref HEAD');
71
+ }
72
+
73
+ function getStatusLines() {
74
+ const output = safeExec('git status --porcelain');
75
+ return output ? output.split('\n').filter(Boolean) : [];
76
+ }
77
+
78
+ function sanitizeSegment(value) {
79
+ return String(value || '')
80
+ .trim()
81
+ .toLowerCase()
82
+ .replace(/[^a-z0-9]+/g, '-')
83
+ .replace(/^-+|-+$/g, '')
84
+ .replace(/-{2,}/g, '-');
85
+ }
86
+
87
+ function detectOwnerPrefix(explicitOwner) {
88
+ if (explicitOwner) {
89
+ return sanitizeSegment(explicitOwner) || 'dev';
90
+ }
91
+
92
+ const rawSignals = [
93
+ safeExec('git config user.name'),
94
+ safeExec('git config user.email'),
95
+ process.env.USERNAME || '',
96
+ process.env.USER || '',
97
+ ]
98
+ .join(' ')
99
+ .toLowerCase();
100
+
101
+ if (/(caio|imori)/.test(rawSignals)) {
102
+ return 'caio';
103
+ }
104
+
105
+ if (/(matheus|soier|sawyer)/.test(rawSignals)) {
106
+ return 'soier';
107
+ }
108
+
109
+ return 'dev';
110
+ }
111
+
112
+ function buildWorkItemSlug(storyId, slug) {
113
+ const sanitizedStory = sanitizeSegment(storyId);
114
+ const sanitizedSlug = sanitizeSegment(slug || storyId);
115
+ if (!sanitizedSlug) {
116
+ return sanitizedStory;
117
+ }
118
+ if (!sanitizedStory) {
119
+ return sanitizedSlug;
120
+ }
121
+ return sanitizedSlug.startsWith(`${sanitizedStory}-`) || sanitizedSlug === sanitizedStory
122
+ ? sanitizedSlug
123
+ : `${sanitizedStory}-${sanitizedSlug}`;
124
+ }
125
+
126
+ function buildBranchName({ owner, type, storyId, slug }) {
127
+ return `${owner}/${sanitizeSegment(type) || 'feat'}/${buildWorkItemSlug(storyId, slug)}`;
128
+ }
129
+
130
+ function buildWorktreePath(worktreeKey) {
131
+ return path.join(process.cwd(), '.sinapse', 'worktrees', worktreeKey);
132
+ }
133
+
134
+ function ensureReadyDefaultBranch(defaultBranch) {
135
+ const branchName = getCurrentBranch();
136
+ const statusLines = getStatusLines();
137
+
138
+ if (branchName !== defaultBranch) {
139
+ throw new Error(
140
+ `Run collab:start from a clean '${defaultBranch}' checkout. Current branch: '${branchName}'.`,
141
+ );
142
+ }
143
+
144
+ if (statusLines.length > 0) {
145
+ throw new Error(
146
+ `Working tree is dirty on '${defaultBranch}'. Commit or stash your changes before opening a new worktree.`,
147
+ );
148
+ }
149
+ }
150
+
151
+ function ensureOnDefaultBranch(defaultBranch) {
152
+ const branchName = getCurrentBranch();
153
+ if (branchName !== defaultBranch) {
154
+ throw new Error(
155
+ `Run this command from '${defaultBranch}'. Current branch: '${branchName}'.`,
156
+ );
157
+ }
158
+ }
159
+
160
+ function ensureUpToDate(defaultBranch) {
161
+ execFileSync('git', ['fetch', 'origin'], {
162
+ stdio: ['ignore', 'ignore', 'pipe'],
163
+ });
164
+
165
+ const localSha = safeExec(`git rev-parse ${defaultBranch}`);
166
+ const remoteSha = safeExec(`git rev-parse origin/${defaultBranch}`);
167
+
168
+ if (localSha && remoteSha && localSha !== remoteSha) {
169
+ execFileSync('git', ['pull', '--ff-only', 'origin', defaultBranch], {
170
+ stdio: 'inherit',
171
+ });
172
+ }
173
+ }
174
+
175
+ function createWorktree({ branchName, defaultBranch, worktreePath }) {
176
+ execFileSync('git', ['worktree', 'add', worktreePath, '-b', branchName, defaultBranch], {
177
+ stdio: 'inherit',
178
+ });
179
+ }
180
+
181
+ function adoptCurrentWorkspace(branchName) {
182
+ execFileSync('git', ['checkout', '-b', branchName], {
183
+ stdio: 'inherit',
184
+ });
185
+ }
186
+
187
+ function runCheck() {
188
+ const defaultBranch = resolveDefaultBranch();
189
+ const currentBranch = getCurrentBranch();
190
+ const statusLines = getStatusLines();
191
+ const owner = detectOwnerPrefix(null);
192
+
193
+ console.log(`Default branch: ${defaultBranch}`);
194
+ console.log(`Current branch: ${currentBranch}`);
195
+ console.log(`Detected maintainer prefix: ${owner}`);
196
+ console.log(`Working tree clean: ${statusLines.length === 0 ? 'yes' : 'no'}`);
197
+ }
198
+
199
+ function main() {
200
+ const args = parseArgs(process.argv.slice(2));
201
+ if (args.check) {
202
+ runCheck();
203
+ return;
204
+ }
205
+
206
+ if (!args.storyId) {
207
+ console.error(
208
+ 'Usage: npm run collab:start -- <story-id> <slug> [--type=feat] [--owner=caio] [--adopt-current]',
209
+ );
210
+ process.exit(1);
211
+ }
212
+
213
+ const defaultBranch = resolveDefaultBranch();
214
+ const owner = detectOwnerPrefix(args.owner);
215
+ const branchName = buildBranchName({
216
+ owner,
217
+ type: args.type,
218
+ storyId: args.storyId,
219
+ slug: args.slug,
220
+ });
221
+ const worktreeKey = sanitizeSegment(branchName.replace(/\//g, '-'));
222
+ const worktreePath = buildWorktreePath(worktreeKey);
223
+
224
+ try {
225
+ if (args.adoptCurrent) {
226
+ ensureOnDefaultBranch(defaultBranch);
227
+ adoptCurrentWorkspace(branchName);
228
+ } else {
229
+ ensureReadyDefaultBranch(defaultBranch);
230
+ ensureUpToDate(defaultBranch);
231
+ createWorktree({ branchName, defaultBranch, worktreePath });
232
+ }
233
+ } catch (error) {
234
+ console.error('');
235
+ console.error(`collab:start failed: ${error.message}`);
236
+ console.error('');
237
+ process.exit(1);
238
+ }
239
+
240
+ console.log('');
241
+ if (args.adoptCurrent) {
242
+ console.log('Current workspace adopted into a safe maintainer branch.');
243
+ console.log(`Branch: ${branchName}`);
244
+ console.log('');
245
+ console.log('Continue working in the current directory, now outside main.');
246
+ } else {
247
+ console.log('Safe maintainer workspace created.');
248
+ console.log(`Branch: ${branchName}`);
249
+ console.log(`Worktree: ${worktreePath}`);
250
+ console.log('');
251
+ console.log(`Next step: cd "${worktreePath}"`);
252
+ }
253
+ console.log('');
254
+ }
255
+
256
+ module.exports = {
257
+ buildBranchName,
258
+ buildWorkItemSlug,
259
+ detectOwnerPrefix,
260
+ parseArgs,
261
+ resolveDefaultBranch,
262
+ sanitizeSegment,
263
+ };
264
+
265
+ if (require.main === module) {
266
+ main();
267
+ }