Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
2746238
feat: [ENG-1929] Temporarily Remove brv init
bao-byterover Apr 5, 2026
e37aac6
Merge branch 'feat/ENG-1929' into proj/git-semantics
bao-byterover Apr 5, 2026
99f44d8
fix: [ENG-1948] treat identical OIDs as ancestor in branch -d merge c…
cuongdo-byterover Apr 5, 2026
184fc6f
Merge pull request #324 from campfirein/fix/ENG-1948
cuongdo-byterover Apr 5, 2026
1136707
feat: [ENG-1929] skip test init
bao-byterover Apr 5, 2026
5a0077d
Merge branch 'feat/ENG-1929' into proj/git-semantics
bao-byterover Apr 5, 2026
d1c3583
feat: [ENG-1905] Store and use name-based URLs instead of UUIDs
bao-byterover Apr 5, 2026
efb6cb5
Merge branch 'proj/git-semantics' into feat/ENG-1905
bao-byterover Apr 5, 2026
620c89b
feat: [ENG-1905] fix issue remote url slug special char
bao-byterover Apr 5, 2026
b33226d
Merge branch 'feat/ENG-1905' into proj/git-semantics
bao-byterover Apr 5, 2026
c6c5e9b
fix:[ENG-1951] TUI help flow and vc commit message parsing
cuongdo-byterover Apr 5, 2026
0d1fbf0
Merge branch 'proj/git-semantics' into fix/ENG-1951
cuongdo-byterover Apr 5, 2026
a685b70
test: update error-messages tests for brv-to-slash rewriting
cuongdo-byterover Apr 5, 2026
4a9bc00
fix: [ENG-1951] minor changes
cuongdo-byterover Apr 5, 2026
e3add8d
Merge pull request #326 from campfirein/fix/ENG-1951
bao-byterover Apr 5, 2026
efb6396
feat: [ENG-1954] brv vc should run even when .brv is not present
bao-byterover Apr 6, 2026
ddb1f22
Merge branch 'feat/ENG-1954' into proj/git-semantics
bao-byterover Apr 6, 2026
69f8465
feat: [ENG-1958] brv --help must show vc in COMMANDS sections
bao-byterover Apr 6, 2026
8b87577
Merge branch 'feat/ENG-1958' into proj/git-semantics
bao-byterover Apr 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
BRV_API_BASE_URL=http://localhost:3000/api/v1
BRV_AUTHORIZATION_URL=http://localhost:3000/api/v1/oidc/authorize
BRV_COGIT_API_BASE_URL=http://localhost:3001/api/v1
BRV_GIT_API_BASE_URL=http://localhost:3001
BRV_GIT_REMOTE_BASE_URL=http://localhost:8080
BRV_ISSUER_URL=http://localhost:3000/api/v1/oidc
BRV_LLM_API_BASE_URL=http://localhost:3002
Expand Down
3 changes: 0 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,6 @@
],
"topicSeparator": " ",
"topics": {
"vc": {
"description": "Version control commands for the context tree"
},
"hub": {
"description": "Browse and manage skills & bundles registry",
"subtopics": {
Expand Down
5 changes: 5 additions & 0 deletions src/oclif/commands/init.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-unreachable */
import {Command, Flags} from '@oclif/core'

import {InitEvents, type InitLocalResponse} from '../../shared/transport/events/init-events.js'
Expand All @@ -14,12 +15,16 @@ export default class Init extends Command {
description: 'Force re-initialization even if already initialized',
}),
}
public static hidden = true

protected getDaemonOptions(): DaemonClientOptions {
return {projectPath: process.cwd()}
}

public async run(): Promise<void> {
this.log('The init command is not available. Use: brv vc init')
return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (code quality): Dead code is suppressed with /* eslint-disable no-unreachable */ at the file level, silencing the linter for the entire file. If the intent is to keep the old implementation for future reference, it would be safer to move it to a comment block or a separate archived file. The current approach is fragile — any genuinely unreachable code added later to this file won't be caught.

If the brv init command is being permanently retired (it's now hidden and redirects users), just delete the old body entirely.

const {flags} = await this.parse(Init)
const daemonOptions = this.getDaemonOptions()

Expand Down
9 changes: 7 additions & 2 deletions src/oclif/commands/vc/commit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@ export default class VcCommit extends Command {
description: 'Commit message',
}),
}
public static strict = false

public async run(): Promise<void> {
const {flags} = await this.parse(VcCommit)
const {argv, flags} = await this.parse(VcCommit)

const {message} = flags
// Support unquoted multi-word messages: brv vc commit -m hello world
const extra = (argv as string[]).join(' ')
const message = flags.message
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (standards): argv as string[] is a type assertion — the coding standards prohibit as Type casts in favour of type guards or proper typing. In oclif with strict: false, this.parse() already returns argv: string[] in the result type; you likely just need to destructure argv without the cast.

Suggested change
const message = flags.message
const extra = argv.join(' ')

? (extra ? `${flags.message} ${extra}` : flags.message)
: (extra || undefined)
if (!message) {
this.error('Usage: brv vc commit -m "<message>"')
}
Expand Down
10 changes: 10 additions & 0 deletions src/oclif/commands/vc/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {Command} from '@oclif/core'

export default class Vc extends Command {
public static description = 'Version control commands for the context tree'
public static examples = ['<%= config.bin %> <%= command.id %> --help']

public async run(): Promise<void> {
await this.config.runCommand('help', ['vc'])
}
}
2 changes: 1 addition & 1 deletion src/oclif/commands/vc/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {type IVcInitResponse, VcEvents} from '../../../shared/transport/events/v
import {formatConnectionError, withDaemonRetry} from '../../lib/daemon-client.js'

export default class VcInit extends Command {
public static description = 'Initialize ByteRover version control'
public static description = 'Initialize ByteRover version control for context tree'
public static examples = ['<%= config.bin %> <%= command.id %>']

public async run(): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion src/oclif/hooks/init/validate-brv-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import {getProjectDataDir} from '../../../server/utils/path-utils.js'
export const SKIP_COMMANDS = new Set<string>([
'--help',
'help',
'init',
'login',
'logout',
'main',
'restart',
'vc',
'vc:clone',
'vc:init',
])
Expand Down
2 changes: 0 additions & 2 deletions src/server/config/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ type EnvironmentConfig = {
authorizationUrl: string
clientId: string
cogitApiBaseUrl: string
gitApiBaseUrl: string
gitRemoteBaseUrl: string
hubRegistryUrl: string
issuerUrl: string
Expand Down Expand Up @@ -57,7 +56,6 @@ export const getCurrentConfig = (): EnvironmentConfig => ({
authorizationUrl: readRequiredEnv('BRV_AUTHORIZATION_URL'),
clientId: DEFAULTS.clientId,
cogitApiBaseUrl: readRequiredEnv('BRV_COGIT_API_BASE_URL'),
gitApiBaseUrl: readRequiredEnv('BRV_GIT_API_BASE_URL'),
gitRemoteBaseUrl: readRequiredEnv('BRV_GIT_REMOTE_BASE_URL'),
hubRegistryUrl: DEFAULTS.hubRegistryUrl,
issuerUrl: readRequiredEnv('BRV_ISSUER_URL'),
Expand Down
10 changes: 10 additions & 0 deletions src/server/core/domain/entities/space.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ interface SpaceParams {
id: string
isDefault: boolean
name: string
slug?: string
teamId: string
teamName: string
teamSlug?: string
}

/**
Expand All @@ -17,8 +19,10 @@ export class Space {
public readonly id: string
public readonly isDefault: boolean
public readonly name: string
public readonly slug: string
public readonly teamId: string
public readonly teamName: string
public readonly teamSlug: string

public constructor(params: SpaceParams) {
if (params.id.trim().length === 0) {
Expand All @@ -40,8 +44,10 @@ export class Space {
this.id = params.id
this.isDefault = params.isDefault
this.name = params.name
this.slug = params.slug ?? params.name
this.teamId = params.teamId
this.teamName = params.teamName
this.teamSlug = params.teamSlug ?? params.teamName
}

/**
Expand Down Expand Up @@ -72,8 +78,10 @@ export class Space {
id: json.id,
isDefault: json.is_default,
name: json.name,
slug: typeof json.slug === 'string' ? json.slug : json.name,
teamId: json.team_id,
teamName: json.team_name,
teamSlug: typeof json.team_slug === 'string' ? json.team_slug : json.team_name,
})
}

Expand All @@ -93,8 +101,10 @@ export class Space {
id: this.id,
isDefault: this.isDefault,
name: this.name,
slug: this.slug,
teamId: this.teamId,
teamName: this.teamName,
teamSlug: this.teamSlug,
}
}
}
5 changes: 5 additions & 0 deletions src/server/core/domain/entities/team.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface TeamParams {
isActive: boolean
isDefault: boolean
name: string
slug: string
updatedAt: Date
}

Expand All @@ -26,6 +27,7 @@ export class Team {
public readonly isActive: boolean
public readonly isDefault: boolean
public readonly name: string
public readonly slug: string
public readonly updatedAt: Date

public constructor(params: TeamParams) {
Expand All @@ -49,6 +51,7 @@ export class Team {
this.isActive = params.isActive
this.isDefault = params.isDefault
this.name = params.name
this.slug = params.slug
this.updatedAt = params.updatedAt
}

Expand Down Expand Up @@ -94,6 +97,7 @@ export class Team {
isActive: json.is_active,
isDefault: json.is_default,
name: json.name,
slug: typeof json.slug === 'string' ? json.slug : json.name,
updatedAt: new Date(json.updated_at),
})
}
Expand All @@ -119,6 +123,7 @@ export class Team {
isActive: this.isActive,
isDefault: this.isDefault,
name: this.name,
slug: this.slug,
updatedAt: this.updatedAt.toISOString(),
}
}
Expand Down
51 changes: 5 additions & 46 deletions src/server/infra/git/cogit-url.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,15 @@
/**
* Build the CoGit Git remote URL for a given team and space.
* Format: {gitApiBaseUrl}/git/{teamId}/{spaceId}.git
* Format: {baseUrl}/{teamName}/{spaceName}.git
*/
export function buildCogitRemoteUrl(baseUrl: string, teamId: string, spaceId: string): string {
export function buildCogitRemoteUrl(baseUrl: string, teamName: string, spaceName: string): string {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (docs): JSDoc says {teamName}/{spaceName} but after this PR the segments are slugs, not display names. Update to {teamSlug}/{spaceSlug} to keep the contract clear.

Suggested change
export function buildCogitRemoteUrl(baseUrl: string, teamName: string, spaceName: string): string {
* Format: {baseUrl}/{teamSlug}/{spaceSlug}.git

const base = baseUrl.replace(/\/$/, '')
return `${base}/git/${teamId}/${spaceId}.git`
return `${base}/${teamName}/${spaceName}.git`
}

/**
* Remove credentials from a URL, returning only the host + path.
*/
export function stripCredentialsFromUrl(url: string): string {
try {
const parsed = new URL(url)
parsed.username = ''
parsed.password = ''
return parsed.toString()
} catch {
return url
}
}

/**
* Parse a URL that contains /git/{segment1}/{segment2}.git
* Returns the two segments and whether they look like UUIDs.
*/
export function parseGitPathUrl(url: string): null | {
areUuids: boolean
segment1: string
segment2: string
} {
try {
const parsed = new URL(url)
const match = parsed.pathname.match(/^\/git\/([^/]+)\/([^/]+?)\.git$/)
if (!match) return null
const segment1 = match[1]
const segment2 = match[2]
return {areUuids: isUuid(segment1) && isUuid(segment2), segment1, segment2}
} catch {
return null
}
}

/**
* Parse a user-facing .git URL to extract team and space names.
* Expected path: /{teamName}/{spaceName}.git (no /git/ prefix)
* Parse a .git URL to extract team and space names.
* Expected path: /{teamName}/{spaceName}.git
*/
export function parseUserFacingUrl(url: string): null | {spaceName: string; teamName: string} {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (docs): Same issue — the property names in the return type {teamName, spaceName} are misleading because these values are parsed from URL path segments, which are slugs. The caller (vc-handler.ts) passes them straight to resolveTeamSpaceNames(parsed.teamName, parsed.spaceName) which then matches against t.slug. Consider renaming to {teamSlug, spaceSlug} to make the intent obvious and prevent future misuse.

try {
Expand All @@ -69,9 +34,3 @@ export function isValidBranchName(name: string): boolean {
return !/\.\.|[~^:?*[\\\u0000-\u001F\u007F]/.test(name)
}

/**
* Check if a string looks like a UUID (v1-v7, with hyphens).
*/
function isUuid(value: string): boolean {
return /^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/i.test(value)
}
1 change: 1 addition & 0 deletions src/server/infra/git/isomorphic-git-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ export class IsomorphicGitService implements IGitService {
const dir = this.requireDirectory(params)
const commitOid = await git.resolveRef({dir, fs, ref: params.commit})
const ancestorOid = await git.resolveRef({dir, fs, ref: params.ancestor})
if (commitOid === ancestorOid) return true
return git.isDescendent({ancestor: ancestorOid, depth: -1, dir, fs, oid: commitOid})
}

Expand Down
1 change: 0 additions & 1 deletion src/server/infra/process/feature-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,6 @@ export async function setupFeatureHandlers({
new VcHandler({
broadcastToProject,
contextTreeService,
gitApiBaseUrl: envConfig.gitApiBaseUrl,
gitRemoteBaseUrl: envConfig.gitRemoteBaseUrl,
gitService,
projectConfigStore,
Expand Down
4 changes: 4 additions & 0 deletions src/server/infra/space/http-space-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type SpaceServiceConfig = {

type Team = {
name: string
slug?: string
}

type SpaceApiResponse = {
Expand All @@ -23,6 +24,7 @@ type SpaceApiResponse = {
is_default: boolean
name: string
size: number
slug?: string
status: string
storage_path: string
team: Team
Expand Down Expand Up @@ -130,8 +132,10 @@ export class HttpSpaceService implements ISpaceService {
id: spaceData.id,
isDefault: spaceData.is_default,
name: spaceData.name,
slug: spaceData.slug,
teamId: spaceData.team_id,
teamName: spaceData.team.name,
teamSlug: spaceData.team.slug,
})
}
}
1 change: 1 addition & 0 deletions src/server/infra/team/http-team-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type TeamApiResponse = {
is_active: boolean
is_default: boolean
name: string
slug?: string
updated_at: string
}

Expand Down
Loading
Loading