tycono 0.1.78 → 0.1.80

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tycono",
3
- "version": "0.1.78",
3
+ "version": "0.1.80",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { readPreferences } from '../services/preferences.js';
4
- import { readConfig } from '../services/company-config.js';
4
+ import { readConfig, resolveCodeRoot } from '../services/company-config.js';
5
5
  import {
6
6
  type OrgTree,
7
7
  type OrgNode,
@@ -102,11 +102,10 @@ export function assembleContext(
102
102
  }
103
103
 
104
104
  // 9. Code Root (코드 프로젝트 경로)
105
- const config = readConfig(companyRoot);
106
- if (config.codeRoot) {
107
- sections.push(`# Code Project
105
+ const codeRoot = resolveCodeRoot(companyRoot);
106
+ sections.push(`# Code Project
108
107
 
109
- The code repository is located at: \`${config.codeRoot}\` (env: $TYCONO_CODE_ROOT)
108
+ The code repository is located at: \`${codeRoot}\` (env: $TYCONO_CODE_ROOT)
110
109
  The AKB (knowledge) directory is at: \`${companyRoot}\` (env: $TYCONO_AKB_ROOT)
111
110
 
112
111
  Use the code repository path for all source code work (reading, writing, building, testing).
@@ -116,7 +115,6 @@ Use the code repository path for all source code work (reading, writing, buildin
116
115
  - **NEVER run \`git worktree add\` in \`$TYCONO_AKB_ROOT\`** — the AKB directory is not a code repository.
117
116
  - Recommended worktree path: \`$TYCONO_CODE_ROOT/.worktrees/{branch-name}\`
118
117
  - Example: \`git worktree add .worktrees/feature-xyz -b feature/xyz\` (from cwd, which is already code repo)`);
119
- }
120
118
 
121
119
  // 10. Pre-Knowledging: 작업 관련 문서 자동 탐색
122
120
  const preKSection = buildPreKnowledgingSection(companyRoot, task);
@@ -4,7 +4,7 @@ import path from 'node:path';
4
4
  import os from 'node:os';
5
5
  import { assembleContext } from '../context-assembler.js';
6
6
  import { getSubordinates } from '../org-tree.js';
7
- import { readConfig } from '../../services/company-config.js';
7
+ import { readConfig, resolveCodeRoot } from '../../services/company-config.js';
8
8
  import { getTokenLedger } from '../../services/token-ledger.js';
9
9
  import type { ExecutionRunner, RunnerConfig, RunnerCallbacks, RunnerHandle, RunnerResult } from './types.js';
10
10
 
@@ -350,12 +350,12 @@ export class ClaudeCliRunner implements ExecutionRunner {
350
350
  cleanEnv.CONSULT_SOURCE_ROLE = roleId;
351
351
 
352
352
  const modelName = config.model ?? 'claude-sonnet-4-5';
353
- // Use codeRoot as cwd if configured, otherwise fall back to companyRoot
354
- const companyConfig = readConfig(companyRoot);
355
- const cwd = companyConfig.codeRoot || companyRoot;
353
+ // Use codeRoot as cwd auto-creates ../{name}-code/ if not configured
354
+ const codeRoot = resolveCodeRoot(companyRoot);
355
+ const cwd = codeRoot;
356
356
 
357
357
  // Inject repo paths so agents never confuse repos
358
- cleanEnv.TYCONO_CODE_ROOT = companyConfig.codeRoot || '';
358
+ cleanEnv.TYCONO_CODE_ROOT = codeRoot;
359
359
  cleanEnv.TYCONO_AKB_ROOT = companyRoot;
360
360
  console.log(`[Runner] Spawning claude -p: role=${roleId}, model=${modelName}, maxTurns=${maxTurns}, jobId=${config.jobId ?? 'none'}, cwd=${cwd}, subordinates=[${subordinates.join(',')}]`);
361
361
 
@@ -16,9 +16,9 @@ saveRouter.get('/status', (req: Request, res: Response, next: NextFunction) => {
16
16
  try {
17
17
  res.json(getGitStatus(COMPANY_ROOT, getRepo(req)));
18
18
  } catch (err) {
19
- // codeRoot not configured is expected for fresh installs — return empty status
20
- if (err instanceof Error && err.message.includes('codeRoot not configured')) {
21
- res.json({ isRepo: false, branch: '', staged: [], unstaged: [], untracked: [], notConfigured: true });
19
+ // Not a git repo (e.g. auto-created code dir) — return empty status
20
+ if (err instanceof Error && (err.message.includes('not a git repository') || err.message.includes('codeRoot'))) {
21
+ res.json({ isRepo: false, branch: '', staged: [], unstaged: [], untracked: [] });
22
22
  return;
23
23
  }
24
24
  next(err);
@@ -44,6 +44,29 @@ function configPath(companyRoot: string): string {
44
44
  return path.join(companyRoot, CONFIG_DIR, CONFIG_FILE);
45
45
  }
46
46
 
47
+ /**
48
+ * Resolve codeRoot: explicit config > auto-generated sibling directory.
49
+ * When codeRoot is not configured, defaults to `../{dirname}-code/` next to companyRoot.
50
+ * Auto-creates the directory if it doesn't exist.
51
+ */
52
+ export function resolveCodeRoot(companyRoot: string): string {
53
+ const config = readConfig(companyRoot);
54
+ if (config.codeRoot) return config.codeRoot;
55
+
56
+ // Auto-generate: ../{folder-name}-code/
57
+ const dirName = path.basename(companyRoot);
58
+ const autoCodeRoot = path.join(path.dirname(companyRoot), `${dirName}-code`);
59
+
60
+ if (!fs.existsSync(autoCodeRoot)) {
61
+ fs.mkdirSync(autoCodeRoot, { recursive: true });
62
+ }
63
+
64
+ // Persist so it's stable across restarts
65
+ writeConfig(companyRoot, { ...config, codeRoot: autoCodeRoot });
66
+
67
+ return autoCodeRoot;
68
+ }
69
+
47
70
  /** Read config from .tycono/config.json. Returns defaults if missing. */
48
71
  export function readConfig(companyRoot: string): CompanyConfig {
49
72
  const p = configPath(companyRoot);
@@ -16,6 +16,7 @@
16
16
  import { execSync } from 'node:child_process';
17
17
  import { readFileSync } from 'node:fs';
18
18
  import { join } from 'node:path';
19
+ import { resolveCodeRoot } from './company-config.js';
19
20
 
20
21
  export type RepoType = 'akb' | 'code';
21
22
 
@@ -87,47 +88,17 @@ const SAVE_PATHS = [
87
88
  'CLAUDE.md',
88
89
  ];
89
90
 
90
- interface TyconoConfig {
91
- companyName: string;
92
- engine: string;
93
- createdAt: string;
94
- codeRoot?: string;
95
- }
96
-
97
- /**
98
- * Read codeRoot from .tycono/config.json
99
- * @param akbRoot - AKB repository root (COMPANY_ROOT)
100
- * @returns codeRoot path if configured, undefined otherwise
101
- */
102
- function getCodeRoot(akbRoot: string): string | undefined {
103
- try {
104
- const configPath = join(akbRoot, '.tycono', 'config.json');
105
- const content = readFileSync(configPath, 'utf-8');
106
- const config: TyconoConfig = JSON.parse(content);
107
- return config.codeRoot;
108
- } catch {
109
- return undefined;
110
- }
111
- }
112
-
113
91
  /**
114
92
  * Resolve repository root based on repo type
115
93
  * @param akbRoot - AKB repository root (COMPANY_ROOT)
116
94
  * @param repo - Repository type ('akb' or 'code')
117
95
  * @returns Resolved repository root path
118
- * @throws Error if repo='code' but codeRoot not configured
119
96
  */
120
97
  function resolveRepoRoot(akbRoot: string, repo: RepoType = 'akb'): string {
121
98
  if (repo === 'akb') {
122
99
  return akbRoot;
123
100
  }
124
-
125
- const codeRoot = getCodeRoot(akbRoot);
126
- if (!codeRoot) {
127
- throw new Error('codeRoot not configured in .tycono/config.json');
128
- }
129
-
130
- return codeRoot;
101
+ return resolveCodeRoot(akbRoot);
131
102
  }
132
103
 
133
104
  /**
@@ -7,7 +7,7 @@ import type { ExecutionRunner } from '../engine/runners/types.js';
7
7
  import { setActivity, updateActivity, completeActivity } from './activity-tracker.js';
8
8
  import type { RunnerResult } from '../engine/runners/types.js';
9
9
  import { estimateCost } from './pricing.js';
10
- import { readConfig, getConversationLimits } from './company-config.js';
10
+ import { readConfig, getConversationLimits, resolveCodeRoot } from './company-config.js';
11
11
  import { postKnowledgingCheck, type KnowledgeDebtItem } from '../engine/knowledge-gate.js';
12
12
  import { earnCoinsInternal } from '../routes/coins.js';
13
13
  import { getSession, createSession, addMessage, updateMessage as updateSessionMessage, appendMessageEvent, type Message, type ImageAttachment } from './session-store.js';
@@ -269,7 +269,7 @@ class JobManager {
269
269
  jobId: job.id,
270
270
  teamStatus,
271
271
  targetRoles: params.targetRoles,
272
- codeRoot: config.codeRoot,
272
+ codeRoot: resolveCodeRoot(COMPANY_ROOT),
273
273
  attachments: params.attachments,
274
274
  env: {
275
275
  ...process.env,
@@ -343,11 +343,14 @@ export function scaffold(config: ScaffoldConfig): string[] {
343
343
  created.push('.gitignore');
344
344
  }
345
345
 
346
- // Write .tycono/config.json (engine + API key)
346
+ // Write .tycono/config.json (engine + API key + codeRoot)
347
+ const slug = config.companyName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') || 'my-company';
348
+ const defaultCodeRoot = path.join(path.dirname(root), `${slug}-code`);
347
349
  if (config.apiKey) {
348
350
  const companyConfig: CompanyConfig = {
349
351
  engine: 'direct-api',
350
352
  apiKey: config.apiKey,
353
+ codeRoot: defaultCodeRoot,
351
354
  };
352
355
  writeConfig(root, companyConfig);
353
356
  created.push('.tycono/config.json');
@@ -355,10 +358,13 @@ export function scaffold(config: ScaffoldConfig): string[] {
355
358
  fs.writeFileSync(path.join(root, '.env'), `ANTHROPIC_API_KEY=${config.apiKey}\n`);
356
359
  created.push('.env');
357
360
  } else {
358
- const companyConfig: CompanyConfig = { engine: 'claude-cli' };
361
+ const companyConfig: CompanyConfig = { engine: 'claude-cli', codeRoot: defaultCodeRoot };
359
362
  writeConfig(root, companyConfig);
360
363
  created.push('.tycono/config.json');
361
364
  }
365
+ // Ensure codeRoot directory exists
366
+ fs.mkdirSync(defaultCodeRoot, { recursive: true });
367
+ created.push(`(code) ${defaultCodeRoot}`);
362
368
 
363
369
  // Save language preference (default: English)
364
370
  mergePreferences(root, { language: config.language || 'en' });