Skip to content

Conversation

@amishne
Copy link
Contributor

@amishne amishne commented Jan 14, 2026

  • Many tools now have workspace and project options that make them better suited to monorepo scenarios.
  • Many errors now reported as exceptions instead of regular messages. They all have informative error messages.
  • The above now uses shared implementation across tools.
  • Tests are now simplified and more consistent.

Tested manually with Gemini-CLI, both in an Angular directory and in a non-Angular parent directory.

@angular-robot angular-robot bot added detected: feature PR contains a feature commit area: @angular/cli labels Jan 14, 2026
@amishne amishne requested review from clydin and dgp1130 January 15, 2026 00:16
@amishne amishne marked this pull request as ready for review January 15, 2026 00:16
@amishne amishne added the action: review The PR is still awaiting reviews from at least one requested reviewer label Jan 15, 2026
Copy link
Collaborator

@dgp1130 dgp1130 left a comment

Choose a reason for hiding this comment

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

Just some minor suggestions, I'm not sure how much of the resolveWorkspaceAndProject implementation was reused so I might be repeating previous feedback. Feel free to ignore / defer anything we've already discussed or which can be followed up on later.

}

const deadline = Date.now() + input.timeout;
async function performWait(devserver: Devserver, timeout: number) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggestion: Use an explicit return type. Otherwise, TS will implicitly union all the return statement types and wouldn't prevent from accidentally returning the wrong type.

https://techhub.social/@develwithoutacause/111842243141199473

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Getting the factory function types is actually trickier than it seems and is cross-cutting across all tools (including ones not included in this change) so I don't want to include it in this PR.

fail('Should have thrown');
} catch (e) {
expect((e as Error).message).toContain('Dev server not found. Currently running servers:');
expect((e as Error).message).toContain("- Project 'app-one' in workspace path '/test'");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider: Should this message contain the port number too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These errors are worded around how the agent should correct its argument for the next call, the port is not needed for that. Also presumably the agent should have all the ports already reported from all the start commands.

);
}

export function createDevServerNotFoundError(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider: Might be better to leave this in devserver-stop.ts since it specifically relates to devserver functionality.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's used for both stop and wait_for_build so I wanted it in a central place. I did move it to devserver.ts though.

'Please provide a project name or set a default project in angular.json. ' +
"You can use 'list_projects' to find available projects.",
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider: Do these helpers need to be exported? Will they ever realistically be called outside this file if resolveWorkspaceAndProject is doing most of the work?

Going a step further, if these errors are only created in one place, would it be more clear to just inline them at their individual call sites?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Inlined them.

Comment on lines 177 to 178
workspacePathInput,
projectNameInput,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggestion: I don't think we need the Input suffix here, I'd just call these workspacePath and projectName.

If the goal is to have a local variable with the same name, you can do that by aliasing in the destructure without altering the public API of the function.

function func({foo: bar}: {foo: string}): string {
    const foo = bar;
    return foo;
}

func({foo: 'test'});

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wanted to separate them because the input varieties are 1-to-1 with the function input, so for example they could be missing, while the non-input versions are non-nullable results of looking the inputs up.

* current directory as the workspace.
* If `projectNameInput` is absent, uses the default project in the workspace.
*/
export async function resolveWorkspaceAndProject({
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggestion: I'm a bit wary of this utils file getting a bit long and have a nebulous purpose. Could we split it up into separate (or at least rename to a well-defined) domain-specific file(s)? Something like projects.ts or workspaces.ts? Could also combined shared-options.ts into the same file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Split into a separate workspace-utils.ts file, now only some small stuff remained in utils.ts.

let workspace: AngularWorkspace;

if (workspacePathInput) {
if (!host.existsSync(workspacePathInput)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider: Can/should we use async APIs here for improved parallelism? Not sure how much of a concern multiple MCP requests would be.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think it's critical here, and if anything I feel like if this becomes noticeable, some sort of caching would be a better direction.

* current directory as the workspace.
* If `projectNameInput` is absent, uses the default project in the workspace.
*/
export async function resolveWorkspaceAndProject({
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider: Can we split this implementation into separate resolveWorkspace and resolveProject functions? I think this would make the internals easier to follow as we can leverage early returns rather than stateful local variables.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My original implementation was split! But then I realized (1) practically all tools want them both together, and (2) it would lead to duplicate lines of code running. So batched them together for now.

let workspace: AngularWorkspace;

if (workspacePathInput) {
if (!host.existsSync(workspacePathInput)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider: Do we really care about this as a distinct error case from ${workspacePathInput}/angular.json as checked below? I'm not sure this is worth the maintenance effort / performance cost of an additional stat call.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wanted very precise error messages for the agent. I think it also helps clarify that the correct input is a workspace path and not an angular.json path. Ack regarding the cost but I don't think the added check is significant here.

workspacePathInput,
projectNameInput,
mcpWorkspace,
workspaceLoader = AngularWorkspace.load,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider: Is this option just for testing purposes? Can we drop it from the public API or at least make it more clearly not for general usage?

I'm thinking you can either directly depend on AngularWorkspace.load and then spy on it in the test, or define this as a test only parameter like:

function resolveWorkspaceAndProject(
  {/* regular options */}: {/* ... */},
  testOnlyOptions: {
    workspaceLoader = AngularWorkspace.load,
  } = {},
): Promise<{ /* ... */}>;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, now spying on it.

@amishne amishne added action: merge The PR is ready for merge by the caretaker and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Jan 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action: merge The PR is ready for merge by the caretaker area: @angular/cli detected: feature PR contains a feature commit

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants