@renseiai/agentfactory 0.8.5 → 0.8.7
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/README.md +2 -2
- package/dist/src/governor/decision-engine.d.ts +7 -0
- package/dist/src/governor/decision-engine.d.ts.map +1 -1
- package/dist/src/governor/decision-engine.js +59 -1
- package/dist/src/governor/governor.d.ts +5 -1
- package/dist/src/governor/governor.d.ts.map +1 -1
- package/dist/src/governor/governor.js +6 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/orchestrator/activity-emitter.d.ts +3 -3
- package/dist/src/orchestrator/activity-emitter.d.ts.map +1 -1
- package/dist/src/orchestrator/activity-emitter.js +1 -1
- package/dist/src/orchestrator/detect-work-type.test.js +25 -16
- package/dist/src/orchestrator/index.d.ts +4 -0
- package/dist/src/orchestrator/index.d.ts.map +1 -1
- package/dist/src/orchestrator/index.js +1 -0
- package/dist/src/orchestrator/issue-tracker-client.d.ts +103 -0
- package/dist/src/orchestrator/issue-tracker-client.d.ts.map +1 -0
- package/dist/src/orchestrator/issue-tracker-client.js +8 -0
- package/dist/src/orchestrator/log-analyzer.d.ts +19 -4
- package/dist/src/orchestrator/log-analyzer.d.ts.map +1 -1
- package/dist/src/orchestrator/log-analyzer.js +26 -50
- package/dist/src/orchestrator/orchestrator-utils.test.js +3 -0
- package/dist/src/orchestrator/orchestrator.d.ts +4 -2
- package/dist/src/orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/src/orchestrator/orchestrator.js +193 -115
- package/dist/src/orchestrator/parse-work-result.d.ts +1 -1
- package/dist/src/orchestrator/parse-work-result.d.ts.map +1 -1
- package/dist/src/orchestrator/parse-work-result.js +3 -1
- package/dist/src/orchestrator/parse-work-result.test.js +9 -0
- package/dist/src/orchestrator/session-logger.d.ts +1 -1
- package/dist/src/orchestrator/session-logger.d.ts.map +1 -1
- package/dist/src/orchestrator/state-recovery.d.ts +1 -1
- package/dist/src/orchestrator/state-recovery.d.ts.map +1 -1
- package/dist/src/orchestrator/state-recovery.js +1 -0
- package/dist/src/orchestrator/state-types.d.ts +1 -1
- package/dist/src/orchestrator/state-types.d.ts.map +1 -1
- package/dist/src/orchestrator/types.d.ts +22 -2
- package/dist/src/orchestrator/types.d.ts.map +1 -1
- package/dist/src/orchestrator/work-types.d.ts +50 -0
- package/dist/src/orchestrator/work-types.d.ts.map +1 -0
- package/dist/src/orchestrator/work-types.js +20 -0
- package/dist/src/templates/registry.d.ts +1 -1
- package/dist/src/templates/registry.test.js +2 -2
- package/dist/src/templates/renderer.d.ts +1 -1
- package/dist/src/templates/types.d.ts +4 -2
- package/dist/src/templates/types.d.ts.map +1 -1
- package/dist/src/templates/types.js +1 -0
- package/dist/src/templates/types.test.js +4 -3
- package/dist/src/tools/index.d.ts +0 -3
- package/dist/src/tools/index.d.ts.map +1 -1
- package/dist/src/tools/index.js +0 -2
- package/dist/src/workflow/index.d.ts +14 -0
- package/dist/src/workflow/index.d.ts.map +1 -0
- package/dist/src/workflow/index.js +10 -0
- package/dist/src/workflow/transition-engine.d.ts +44 -0
- package/dist/src/workflow/transition-engine.d.ts.map +1 -0
- package/dist/src/workflow/transition-engine.js +106 -0
- package/dist/src/workflow/transition-engine.test.d.ts +2 -0
- package/dist/src/workflow/transition-engine.test.d.ts.map +1 -0
- package/dist/src/workflow/transition-engine.test.js +313 -0
- package/dist/src/workflow/workflow-loader.d.ts +21 -0
- package/dist/src/workflow/workflow-loader.d.ts.map +1 -0
- package/dist/src/workflow/workflow-loader.js +40 -0
- package/dist/src/workflow/workflow-loader.test.d.ts +2 -0
- package/dist/src/workflow/workflow-loader.test.d.ts.map +1 -0
- package/dist/src/workflow/workflow-loader.test.js +134 -0
- package/dist/src/workflow/workflow-registry.d.ts +56 -0
- package/dist/src/workflow/workflow-registry.d.ts.map +1 -0
- package/dist/src/workflow/workflow-registry.js +107 -0
- package/dist/src/workflow/workflow-registry.test.d.ts +2 -0
- package/dist/src/workflow/workflow-registry.test.d.ts.map +1 -0
- package/dist/src/workflow/workflow-registry.test.js +201 -0
- package/dist/src/workflow/workflow-types.d.ts +269 -0
- package/dist/src/workflow/workflow-types.d.ts.map +1 -0
- package/dist/src/workflow/workflow-types.js +88 -0
- package/dist/src/workflow/workflow-types.test.d.ts +2 -0
- package/dist/src/workflow/workflow-types.test.d.ts.map +1 -0
- package/dist/src/workflow/workflow-types.test.js +440 -0
- package/package.json +3 -4
- package/dist/src/linear-cli.d.ts +0 -38
- package/dist/src/linear-cli.d.ts.map +0 -1
- package/dist/src/linear-cli.js +0 -674
- package/dist/src/tools/linear-runner.d.ts +0 -34
- package/dist/src/tools/linear-runner.d.ts.map +0 -1
- package/dist/src/tools/linear-runner.js +0 -700
- package/dist/src/tools/plugins/linear.d.ts +0 -9
- package/dist/src/tools/plugins/linear.d.ts.map +0 -1
- package/dist/src/tools/plugins/linear.js +0 -138
|
@@ -14,14 +14,13 @@ import { createHeartbeatWriter, getHeartbeatIntervalFromEnv } from './heartbeat-
|
|
|
14
14
|
import { createProgressLogger } from './progress-logger.js';
|
|
15
15
|
import { createSessionLogger } from './session-logger.js';
|
|
16
16
|
import { isSessionLoggingEnabled, getLogAnalysisConfig } from './log-config.js';
|
|
17
|
-
import { createLinearAgentClient, createAgentSession, buildCompletionComments, STATUS_WORK_TYPE_MAP, WORK_TYPE_START_STATUS, WORK_TYPE_COMPLETE_STATUS, WORK_TYPE_FAIL_STATUS, TERMINAL_STATUSES, WORK_TYPES_REQUIRING_WORKTREE, } from '@renseiai/agentfactory-linear';
|
|
18
17
|
import { parseWorkResult } from './parse-work-result.js';
|
|
19
18
|
import { createActivityEmitter } from './activity-emitter.js';
|
|
20
19
|
import { createApiActivityEmitter } from './api-activity-emitter.js';
|
|
21
20
|
import { createLogger } from '../logger.js';
|
|
22
21
|
import { TemplateRegistry, createToolPermissionAdapter } from '../templates/index.js';
|
|
23
22
|
import { loadRepositoryConfig, getProjectConfig, getProvidersConfig } from '../config/index.js';
|
|
24
|
-
import { ToolRegistry
|
|
23
|
+
import { ToolRegistry } from '../tools/index.js';
|
|
25
24
|
// Default inactivity timeout: 5 minutes
|
|
26
25
|
const DEFAULT_INACTIVITY_TIMEOUT_MS = 300000;
|
|
27
26
|
// Default max session timeout: unlimited (undefined)
|
|
@@ -442,6 +441,46 @@ Dependencies are symlinked from the main repo by the orchestrator. Do NOT run pn
|
|
|
442
441
|
If you encounter a specific "Cannot find module" error, run it SYNCHRONOUSLY
|
|
443
442
|
(never with run_in_background). Never use sleep or polling loops to wait for commands.
|
|
444
443
|
|
|
444
|
+
IMPORTANT: If you encounter "exceeds maximum allowed tokens" error when reading files:
|
|
445
|
+
- Use Grep to search for specific code patterns instead of reading entire files
|
|
446
|
+
- Use Read with offset/limit parameters to paginate through large files
|
|
447
|
+
- Avoid reading auto-generated files like payload-types.ts (use Grep instead)
|
|
448
|
+
See the "Working with Large Files" section in the project documentation (CLAUDE.md / AGENTS.md) for details.${LINEAR_CLI_INSTRUCTION}`;
|
|
449
|
+
break;
|
|
450
|
+
case 'inflight-coordination':
|
|
451
|
+
basePrompt = `Resume coordination of sub-issue execution for parent issue ${identifier}.
|
|
452
|
+
Check sub-issue statuses, continue work on incomplete sub-issues, and create a PR when all are done.
|
|
453
|
+
|
|
454
|
+
SUB-ISSUE STATUS MANAGEMENT:
|
|
455
|
+
You MUST update sub-issue statuses in Linear as work progresses:
|
|
456
|
+
- When starting work on a sub-issue: pnpm af-linear update-sub-issue <id> --state Started
|
|
457
|
+
- When a sub-agent completes a sub-issue: pnpm af-linear update-sub-issue <id> --state Finished --comment "Completed by coordinator agent"
|
|
458
|
+
- If a sub-agent fails on a sub-issue: pnpm af-linear create-comment <sub-issue-id> --body "Sub-agent failed: <reason>"
|
|
459
|
+
|
|
460
|
+
COMPLETION VERIFICATION:
|
|
461
|
+
Before marking the parent issue as complete, verify ALL sub-issues are in Finished status:
|
|
462
|
+
pnpm af-linear list-sub-issue-statuses ${identifier}
|
|
463
|
+
If any sub-issue is not Finished, report the failure and do not mark the parent as complete.
|
|
464
|
+
|
|
465
|
+
SUB-AGENT SAFETY RULES (CRITICAL):
|
|
466
|
+
This is a SHARED WORKTREE. Multiple sub-agents run concurrently in this directory.
|
|
467
|
+
Every sub-agent prompt you construct MUST include these rules:
|
|
468
|
+
|
|
469
|
+
1. NEVER run: git worktree remove, git worktree prune
|
|
470
|
+
2. NEVER run: git checkout, git switch (to a different branch)
|
|
471
|
+
3. NEVER run: git reset --hard, git clean -fd, git restore .
|
|
472
|
+
4. NEVER delete or modify the .git file in the worktree root
|
|
473
|
+
5. Only the orchestrator manages worktree lifecycle
|
|
474
|
+
6. Work only on files relevant to your sub-issue to minimize conflicts
|
|
475
|
+
7. Commit changes with descriptive messages before reporting completion
|
|
476
|
+
|
|
477
|
+
Prefix every sub-agent prompt with: "SHARED WORKTREE — DO NOT MODIFY GIT STATE"
|
|
478
|
+
|
|
479
|
+
DEPENDENCY INSTALLATION:
|
|
480
|
+
Dependencies are symlinked from the main repo by the orchestrator. Do NOT run pnpm install.
|
|
481
|
+
If you encounter a specific "Cannot find module" error, run it SYNCHRONOUSLY
|
|
482
|
+
(never with run_in_background). Never use sleep or polling loops to wait for commands.
|
|
483
|
+
|
|
445
484
|
IMPORTANT: If you encounter "exceeds maximum allowed tokens" error when reading files:
|
|
446
485
|
- Use Grep to search for specific code patterns instead of reading entire files
|
|
447
486
|
- Use Read with offset/limit parameters to paginate through large files
|
|
@@ -651,6 +690,7 @@ const WORK_TYPE_SUFFIX = {
|
|
|
651
690
|
'backlog-creation': 'BC',
|
|
652
691
|
development: 'DEV',
|
|
653
692
|
inflight: 'INF',
|
|
693
|
+
'inflight-coordination': 'INF-COORD',
|
|
654
694
|
coordination: 'COORD',
|
|
655
695
|
qa: 'QA',
|
|
656
696
|
acceptance: 'AC',
|
|
@@ -678,8 +718,9 @@ export function getWorktreeIdentifier(issueIdentifier, workType) {
|
|
|
678
718
|
* being dispatched as 'development' (which uses the wrong template and
|
|
679
719
|
* produces no sub-agent orchestration).
|
|
680
720
|
*/
|
|
681
|
-
export function detectWorkType(statusName, isParent) {
|
|
682
|
-
|
|
721
|
+
export function detectWorkType(statusName, isParent, statusToWorkType) {
|
|
722
|
+
const mapping = statusToWorkType ?? {};
|
|
723
|
+
let workType = mapping[statusName] ?? 'development';
|
|
683
724
|
console.log(`Auto-detected work type: ${workType} (from status: ${statusName})`);
|
|
684
725
|
if (isParent) {
|
|
685
726
|
if (workType === 'development')
|
|
@@ -688,6 +729,8 @@ export function detectWorkType(statusName, isParent) {
|
|
|
688
729
|
workType = 'qa-coordination';
|
|
689
730
|
else if (workType === 'acceptance')
|
|
690
731
|
workType = 'acceptance-coordination';
|
|
732
|
+
else if (workType === 'inflight')
|
|
733
|
+
workType = 'inflight-coordination';
|
|
691
734
|
else if (workType === 'refinement')
|
|
692
735
|
workType = 'refinement-coordination';
|
|
693
736
|
console.log(`Upgraded to coordination work type: ${workType} (parent issue)`);
|
|
@@ -697,6 +740,7 @@ export function detectWorkType(statusName, isParent) {
|
|
|
697
740
|
export class AgentOrchestrator {
|
|
698
741
|
config;
|
|
699
742
|
client;
|
|
743
|
+
statusMappings;
|
|
700
744
|
events;
|
|
701
745
|
activeAgents = new Map();
|
|
702
746
|
agentHandles = new Map();
|
|
@@ -737,10 +781,15 @@ export class AgentOrchestrator {
|
|
|
737
781
|
validateCommand;
|
|
738
782
|
// Tool plugin registry for in-process agent tools
|
|
739
783
|
toolRegistry;
|
|
784
|
+
// Git repository root for running git commands (resolved from worktreePath or cwd)
|
|
785
|
+
gitRoot;
|
|
740
786
|
constructor(config = {}, events = {}) {
|
|
741
|
-
|
|
742
|
-
if (!
|
|
743
|
-
|
|
787
|
+
// Validate that an issue tracker client is available
|
|
788
|
+
if (!config.issueTrackerClient) {
|
|
789
|
+
const apiKey = config.linearApiKey ?? process.env.LINEAR_API_KEY;
|
|
790
|
+
if (!apiKey) {
|
|
791
|
+
throw new Error('Either issueTrackerClient or LINEAR_API_KEY is required');
|
|
792
|
+
}
|
|
744
793
|
}
|
|
745
794
|
// Parse timeout config from environment variables (can be overridden by config)
|
|
746
795
|
const envInactivityTimeout = process.env.AGENT_INACTIVITY_TIMEOUT_MS
|
|
@@ -752,7 +801,7 @@ export class AgentOrchestrator {
|
|
|
752
801
|
this.config = {
|
|
753
802
|
...DEFAULT_CONFIG,
|
|
754
803
|
...config,
|
|
755
|
-
linearApiKey:
|
|
804
|
+
linearApiKey: config.linearApiKey ?? process.env.LINEAR_API_KEY ?? '',
|
|
756
805
|
streamConfig: {
|
|
757
806
|
...DEFAULT_CONFIG.streamConfig,
|
|
758
807
|
...config.streamConfig,
|
|
@@ -763,11 +812,15 @@ export class AgentOrchestrator {
|
|
|
763
812
|
inactivityTimeoutMs: config.inactivityTimeoutMs ?? envInactivityTimeout ?? DEFAULT_CONFIG.inactivityTimeoutMs,
|
|
764
813
|
maxSessionTimeoutMs: config.maxSessionTimeoutMs ?? envMaxSessionTimeout ?? DEFAULT_CONFIG.maxSessionTimeoutMs,
|
|
765
814
|
};
|
|
815
|
+
// Resolve git root from worktreePath (which may point to a different repo than cwd)
|
|
816
|
+
this.gitRoot = findRepoRoot(resolve(this.config.worktreePath)) ?? findRepoRoot(process.cwd()) ?? process.cwd();
|
|
766
817
|
// Validate git remote matches configured repository (if set)
|
|
767
818
|
if (this.config.repository) {
|
|
768
|
-
validateGitRemote(this.config.repository);
|
|
819
|
+
validateGitRemote(this.config.repository, this.gitRoot);
|
|
769
820
|
}
|
|
770
|
-
|
|
821
|
+
// Use injected client or fail (caller must provide one)
|
|
822
|
+
this.client = config.issueTrackerClient;
|
|
823
|
+
this.statusMappings = config.statusMappings;
|
|
771
824
|
this.events = events;
|
|
772
825
|
// Initialize default agent provider — per-spawn resolution may override
|
|
773
826
|
const providerName = resolveProviderName({ project: config.project });
|
|
@@ -779,8 +832,8 @@ export class AgentOrchestrator {
|
|
|
779
832
|
if (config.templateDir) {
|
|
780
833
|
templateDirs.push(config.templateDir);
|
|
781
834
|
}
|
|
782
|
-
// Auto-detect .agentfactory/templates/ in
|
|
783
|
-
const projectTemplateDir = resolve(
|
|
835
|
+
// Auto-detect .agentfactory/templates/ in target repo
|
|
836
|
+
const projectTemplateDir = resolve(this.gitRoot, '.agentfactory', 'templates');
|
|
784
837
|
if (existsSync(projectTemplateDir) && !templateDirs.includes(projectTemplateDir)) {
|
|
785
838
|
templateDirs.push(projectTemplateDir);
|
|
786
839
|
}
|
|
@@ -797,7 +850,7 @@ export class AgentOrchestrator {
|
|
|
797
850
|
}
|
|
798
851
|
// Auto-load .agentfactory/config.yaml from repository root
|
|
799
852
|
try {
|
|
800
|
-
const repoRoot =
|
|
853
|
+
const repoRoot = this.gitRoot;
|
|
801
854
|
if (repoRoot) {
|
|
802
855
|
const repoConfig = loadRepositoryConfig(repoRoot);
|
|
803
856
|
if (repoConfig) {
|
|
@@ -805,7 +858,7 @@ export class AgentOrchestrator {
|
|
|
805
858
|
// Use repository from config as fallback if not set in OrchestratorConfig
|
|
806
859
|
if (!this.config.repository && repoConfig.repository) {
|
|
807
860
|
this.config.repository = repoConfig.repository;
|
|
808
|
-
validateGitRemote(this.config.repository);
|
|
861
|
+
validateGitRemote(this.config.repository, this.gitRoot);
|
|
809
862
|
}
|
|
810
863
|
// Store allowedProjects for backlog filtering
|
|
811
864
|
if (repoConfig.projectPaths) {
|
|
@@ -845,9 +898,13 @@ export class AgentOrchestrator {
|
|
|
845
898
|
catch (err) {
|
|
846
899
|
console.warn('[orchestrator] Failed to load .agentfactory/config.yaml:', err instanceof Error ? err.message : err);
|
|
847
900
|
}
|
|
848
|
-
// Initialize tool plugin registry with
|
|
901
|
+
// Initialize tool plugin registry with injected plugins
|
|
849
902
|
this.toolRegistry = new ToolRegistry();
|
|
850
|
-
|
|
903
|
+
if (config.toolPlugins) {
|
|
904
|
+
for (const plugin of config.toolPlugins) {
|
|
905
|
+
this.toolRegistry.register(plugin);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
851
908
|
}
|
|
852
909
|
/**
|
|
853
910
|
* Update the last activity timestamp for an agent (for inactivity timeout tracking)
|
|
@@ -891,86 +948,63 @@ export class AgentOrchestrator {
|
|
|
891
948
|
*/
|
|
892
949
|
async detectWorkType(issueId, statusName) {
|
|
893
950
|
const isParent = await this.client.isParentIssue(issueId);
|
|
894
|
-
return detectWorkType(statusName, isParent);
|
|
951
|
+
return detectWorkType(statusName, isParent, this.statusMappings.statusToWorkType);
|
|
895
952
|
}
|
|
896
953
|
/**
|
|
897
954
|
* Get backlog issues for the configured project
|
|
898
955
|
*/
|
|
899
956
|
async getBacklogIssues(limit) {
|
|
900
957
|
const maxIssues = limit ?? this.config.maxConcurrent;
|
|
901
|
-
//
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
const projectRepoUrl = await clientAny.getProjectRepositoryUrl(projects.nodes[0].id);
|
|
917
|
-
if (projectRepoUrl) {
|
|
918
|
-
const normalizedProjectRepo = projectRepoUrl
|
|
919
|
-
.replace(/^https?:\/\//, '')
|
|
920
|
-
.replace(/\.git$/, '');
|
|
921
|
-
const normalizedConfigRepo = this.config.repository
|
|
922
|
-
.replace(/^https?:\/\//, '')
|
|
923
|
-
.replace(/\.git$/, '');
|
|
924
|
-
if (!normalizedProjectRepo.includes(normalizedConfigRepo) && !normalizedConfigRepo.includes(normalizedProjectRepo)) {
|
|
925
|
-
console.warn(`Warning: Project '${this.config.project}' repository metadata '${projectRepoUrl}' ` +
|
|
926
|
-
`does not match configured repository '${this.config.repository}'. Skipping issues.`);
|
|
927
|
-
return [];
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
catch (error) {
|
|
932
|
-
// Non-fatal: log warning but continue if metadata check fails
|
|
933
|
-
console.warn('Warning: Could not check project repository metadata:', error instanceof Error ? error.message : String(error));
|
|
958
|
+
// Cross-reference project repo metadata with config
|
|
959
|
+
if (this.config.project && this.config.repository) {
|
|
960
|
+
try {
|
|
961
|
+
const projectRepoUrl = await this.client.getProjectRepositoryUrl(this.config.project);
|
|
962
|
+
if (projectRepoUrl) {
|
|
963
|
+
const normalizedProjectRepo = projectRepoUrl
|
|
964
|
+
.replace(/^https?:\/\//, '')
|
|
965
|
+
.replace(/\.git$/, '');
|
|
966
|
+
const normalizedConfigRepo = this.config.repository
|
|
967
|
+
.replace(/^https?:\/\//, '')
|
|
968
|
+
.replace(/\.git$/, '');
|
|
969
|
+
if (!normalizedProjectRepo.includes(normalizedConfigRepo) && !normalizedConfigRepo.includes(normalizedProjectRepo)) {
|
|
970
|
+
console.warn(`Warning: Project '${this.config.project}' repository metadata '${projectRepoUrl}' ` +
|
|
971
|
+
`does not match configured repository '${this.config.repository}'. Skipping issues.`);
|
|
972
|
+
return [];
|
|
934
973
|
}
|
|
935
974
|
}
|
|
936
975
|
}
|
|
976
|
+
catch (error) {
|
|
977
|
+
// Non-fatal: log warning but continue if metadata check fails
|
|
978
|
+
console.warn('Warning: Could not check project repository metadata:', error instanceof Error ? error.message : String(error));
|
|
979
|
+
}
|
|
937
980
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
981
|
+
// Query issues using the abstract client
|
|
982
|
+
const allIssues = await this.client.queryIssues({
|
|
983
|
+
project: this.config.project,
|
|
984
|
+
status: 'Backlog',
|
|
985
|
+
maxResults: maxIssues * 2, // Fetch extra to account for filtering
|
|
941
986
|
});
|
|
942
987
|
const results = [];
|
|
943
|
-
for (const issue of
|
|
988
|
+
for (const issue of allIssues) {
|
|
944
989
|
if (results.length >= maxIssues)
|
|
945
990
|
break;
|
|
946
991
|
// Filter by allowedProjects from .agentfactory/config.yaml
|
|
947
|
-
let resolvedProjectName;
|
|
948
992
|
if (this.allowedProjects && this.allowedProjects.length > 0) {
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
if (!projectName || !this.allowedProjects.includes(projectName)) {
|
|
952
|
-
console.warn(`[orchestrator] Skipping issue ${issue.identifier} — project "${projectName ?? '(none)'}" is not in allowedProjects: [${this.allowedProjects.join(', ')}]`);
|
|
993
|
+
if (!issue.projectName || !this.allowedProjects.includes(issue.projectName)) {
|
|
994
|
+
console.warn(`[orchestrator] Skipping issue ${issue.identifier} — project "${issue.projectName ?? '(none)'}" is not in allowedProjects: [${this.allowedProjects.join(', ')}]`);
|
|
953
995
|
continue;
|
|
954
996
|
}
|
|
955
|
-
resolvedProjectName = projectName;
|
|
956
|
-
}
|
|
957
|
-
// Resolve project name for path scoping even when not filtering by allowedProjects
|
|
958
|
-
if (!resolvedProjectName && this.projectPaths) {
|
|
959
|
-
const project = await issue.project;
|
|
960
|
-
resolvedProjectName = project?.name;
|
|
961
997
|
}
|
|
962
|
-
const labels = await issue.labels();
|
|
963
|
-
const team = await issue.team;
|
|
964
998
|
results.push({
|
|
965
999
|
id: issue.id,
|
|
966
1000
|
identifier: issue.identifier,
|
|
967
1001
|
title: issue.title,
|
|
968
|
-
description: issue.description
|
|
1002
|
+
description: issue.description,
|
|
969
1003
|
url: issue.url,
|
|
970
1004
|
priority: issue.priority,
|
|
971
|
-
labels: labels
|
|
972
|
-
teamName:
|
|
973
|
-
projectName:
|
|
1005
|
+
labels: issue.labels,
|
|
1006
|
+
teamName: issue.teamName,
|
|
1007
|
+
projectName: issue.projectName,
|
|
974
1008
|
});
|
|
975
1009
|
}
|
|
976
1010
|
// Sort by priority (lower number = higher priority, 0 means no priority -> goes last)
|
|
@@ -1075,6 +1109,7 @@ export class AgentOrchestrator {
|
|
|
1075
1109
|
const output = execSync('git worktree list --porcelain', {
|
|
1076
1110
|
stdio: 'pipe',
|
|
1077
1111
|
encoding: 'utf-8',
|
|
1112
|
+
cwd: this.gitRoot,
|
|
1078
1113
|
});
|
|
1079
1114
|
const mainTreeMatch = output.match(/^worktree (.+)$/m);
|
|
1080
1115
|
if (mainTreeMatch) {
|
|
@@ -1134,7 +1169,7 @@ export class AgentOrchestrator {
|
|
|
1134
1169
|
if (!existsSync(conflictPath)) {
|
|
1135
1170
|
// Directory doesn't exist - just prune git's worktree list
|
|
1136
1171
|
try {
|
|
1137
|
-
execSync('git worktree prune', { stdio: 'pipe', encoding: 'utf-8' });
|
|
1172
|
+
execSync('git worktree prune', { stdio: 'pipe', encoding: 'utf-8', cwd: this.gitRoot });
|
|
1138
1173
|
console.log(`Pruned stale worktree reference for branch ${branchName}`);
|
|
1139
1174
|
return true;
|
|
1140
1175
|
}
|
|
@@ -1203,6 +1238,7 @@ export class AgentOrchestrator {
|
|
|
1203
1238
|
execSync(`git worktree remove "${conflictPath}" --force`, {
|
|
1204
1239
|
stdio: 'pipe',
|
|
1205
1240
|
encoding: 'utf-8',
|
|
1241
|
+
cwd: this.gitRoot,
|
|
1206
1242
|
});
|
|
1207
1243
|
console.log(`Removed stale worktree: ${conflictPath}`);
|
|
1208
1244
|
return true;
|
|
@@ -1219,7 +1255,7 @@ export class AgentOrchestrator {
|
|
|
1219
1255
|
// this path is inside .worktrees/ and is not the main tree)
|
|
1220
1256
|
try {
|
|
1221
1257
|
execSync(`rm -rf "${conflictPath}"`, { stdio: 'pipe', encoding: 'utf-8' });
|
|
1222
|
-
execSync('git worktree prune', { stdio: 'pipe', encoding: 'utf-8' });
|
|
1258
|
+
execSync('git worktree prune', { stdio: 'pipe', encoding: 'utf-8', cwd: this.gitRoot });
|
|
1223
1259
|
console.log(`Force-removed stale worktree: ${conflictPath}`);
|
|
1224
1260
|
return true;
|
|
1225
1261
|
}
|
|
@@ -1264,7 +1300,7 @@ export class AgentOrchestrator {
|
|
|
1264
1300
|
}
|
|
1265
1301
|
// Prune any stale worktrees first (handles deleted directories)
|
|
1266
1302
|
try {
|
|
1267
|
-
execSync('git worktree prune', { stdio: 'pipe', encoding: 'utf-8' });
|
|
1303
|
+
execSync('git worktree prune', { stdio: 'pipe', encoding: 'utf-8', cwd: this.gitRoot });
|
|
1268
1304
|
}
|
|
1269
1305
|
catch {
|
|
1270
1306
|
// Ignore prune errors
|
|
@@ -1306,6 +1342,7 @@ export class AgentOrchestrator {
|
|
|
1306
1342
|
execSync(`git worktree add "${worktreePath}" -b ${branchName} ${baseBranch}`, {
|
|
1307
1343
|
stdio: 'pipe',
|
|
1308
1344
|
encoding: 'utf-8',
|
|
1345
|
+
cwd: this.gitRoot,
|
|
1309
1346
|
});
|
|
1310
1347
|
}
|
|
1311
1348
|
catch (error) {
|
|
@@ -1332,6 +1369,7 @@ export class AgentOrchestrator {
|
|
|
1332
1369
|
execSync(`git worktree add "${worktreePath}" ${branchName}`, {
|
|
1333
1370
|
stdio: 'pipe',
|
|
1334
1371
|
encoding: 'utf-8',
|
|
1372
|
+
cwd: this.gitRoot,
|
|
1335
1373
|
});
|
|
1336
1374
|
}
|
|
1337
1375
|
catch (innerError) {
|
|
@@ -1366,7 +1404,7 @@ export class AgentOrchestrator {
|
|
|
1366
1404
|
if (existsSync(worktreePath)) {
|
|
1367
1405
|
execSync(`rm -rf "${worktreePath}"`, { stdio: 'pipe', encoding: 'utf-8' });
|
|
1368
1406
|
}
|
|
1369
|
-
execSync('git worktree prune', { stdio: 'pipe', encoding: 'utf-8' });
|
|
1407
|
+
execSync('git worktree prune', { stdio: 'pipe', encoding: 'utf-8', cwd: this.gitRoot });
|
|
1370
1408
|
}
|
|
1371
1409
|
catch {
|
|
1372
1410
|
// Ignore cleanup errors
|
|
@@ -1399,13 +1437,14 @@ export class AgentOrchestrator {
|
|
|
1399
1437
|
execSync(`git worktree remove "${worktreePath}" --force`, {
|
|
1400
1438
|
stdio: 'pipe',
|
|
1401
1439
|
encoding: 'utf-8',
|
|
1440
|
+
cwd: this.gitRoot,
|
|
1402
1441
|
});
|
|
1403
1442
|
}
|
|
1404
1443
|
catch (error) {
|
|
1405
1444
|
console.warn(`Failed to remove worktree via git, trying fallback:`, error);
|
|
1406
1445
|
try {
|
|
1407
1446
|
execSync(`rm -rf "${worktreePath}"`, { stdio: 'pipe', encoding: 'utf-8' });
|
|
1408
|
-
execSync('git worktree prune', { stdio: 'pipe', encoding: 'utf-8' });
|
|
1447
|
+
execSync('git worktree prune', { stdio: 'pipe', encoding: 'utf-8', cwd: this.gitRoot });
|
|
1409
1448
|
}
|
|
1410
1449
|
catch (fallbackError) {
|
|
1411
1450
|
console.warn(`Fallback worktree removal also failed:`, fallbackError);
|
|
@@ -1415,7 +1454,7 @@ export class AgentOrchestrator {
|
|
|
1415
1454
|
else {
|
|
1416
1455
|
// Directory gone but git may still track it
|
|
1417
1456
|
try {
|
|
1418
|
-
execSync('git worktree prune', { stdio: 'pipe', encoding: 'utf-8' });
|
|
1457
|
+
execSync('git worktree prune', { stdio: 'pipe', encoding: 'utf-8', cwd: this.gitRoot });
|
|
1419
1458
|
}
|
|
1420
1459
|
catch {
|
|
1421
1460
|
// Ignore
|
|
@@ -1792,8 +1831,7 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
|
|
|
1792
1831
|
else {
|
|
1793
1832
|
// Direct Linear API - only works with OAuth tokens (not API keys)
|
|
1794
1833
|
// This will fail for createAgentActivity calls but works for comments
|
|
1795
|
-
const session =
|
|
1796
|
-
client: this.client.linearClient,
|
|
1834
|
+
const session = this.client.createSession({
|
|
1797
1835
|
issueId,
|
|
1798
1836
|
sessionId,
|
|
1799
1837
|
autoTransition: false, // Orchestrator handles transitions
|
|
@@ -1847,7 +1885,7 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
|
|
|
1847
1885
|
// Set work type so agent knows what kind of work it's doing
|
|
1848
1886
|
env.LINEAR_WORK_TYPE = workType;
|
|
1849
1887
|
// Flag shared worktree for coordination mode so sub-agents know not to modify git state
|
|
1850
|
-
if (workType === 'coordination' || workType === 'qa-coordination' || workType === 'acceptance-coordination' || workType === 'refinement-coordination') {
|
|
1888
|
+
if (workType === 'coordination' || workType === 'inflight-coordination' || workType === 'qa-coordination' || workType === 'acceptance-coordination' || workType === 'refinement-coordination') {
|
|
1851
1889
|
env.SHARED_WORKTREE = 'true';
|
|
1852
1890
|
}
|
|
1853
1891
|
// Set Claude Code Task List ID for intra-issue task coordination
|
|
@@ -1866,7 +1904,7 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
|
|
|
1866
1904
|
// Coordinators need significantly more turns than standard agents
|
|
1867
1905
|
// since they spawn sub-agents and poll their status repeatedly.
|
|
1868
1906
|
// Inflight also gets the bump — it may be resuming coordination work.
|
|
1869
|
-
const needsMoreTurns = workType === 'coordination' || workType === 'qa-coordination' || workType === 'acceptance-coordination' || workType === 'refinement-coordination' || workType === 'inflight';
|
|
1907
|
+
const needsMoreTurns = workType === 'coordination' || workType === 'inflight-coordination' || workType === 'qa-coordination' || workType === 'acceptance-coordination' || workType === 'refinement-coordination' || workType === 'inflight';
|
|
1870
1908
|
const maxTurns = needsMoreTurns ? 200 : undefined;
|
|
1871
1909
|
// Spawn agent via provider interface
|
|
1872
1910
|
const spawnConfig = {
|
|
@@ -1977,11 +2015,11 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
|
|
|
1977
2015
|
}
|
|
1978
2016
|
agent.workResult = workResult;
|
|
1979
2017
|
if (workResult === 'passed') {
|
|
1980
|
-
targetStatus =
|
|
2018
|
+
targetStatus = this.statusMappings.workTypeCompleteStatus[workType];
|
|
1981
2019
|
log?.info('Work result: passed, promoting', { workType, targetStatus });
|
|
1982
2020
|
}
|
|
1983
2021
|
else if (workResult === 'failed') {
|
|
1984
|
-
targetStatus =
|
|
2022
|
+
targetStatus = this.statusMappings.workTypeFailStatus[workType];
|
|
1985
2023
|
log?.info('Work result: failed, transitioning to fail status', { workType, targetStatus });
|
|
1986
2024
|
}
|
|
1987
2025
|
else {
|
|
@@ -2009,8 +2047,39 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
|
|
|
2009
2047
|
}
|
|
2010
2048
|
}
|
|
2011
2049
|
else {
|
|
2012
|
-
// Non-QA/acceptance:
|
|
2013
|
-
|
|
2050
|
+
// Non-QA/acceptance: promote on completion, but validate code-producing work types first
|
|
2051
|
+
const isCodeProducing = workType === 'development' || workType === 'inflight';
|
|
2052
|
+
if (isCodeProducing && agent.worktreePath && !agent.pullRequestUrl) {
|
|
2053
|
+
// Code-producing agent completed without a detected PR — check for commits
|
|
2054
|
+
const incompleteCheck = checkForIncompleteWork(agent.worktreePath);
|
|
2055
|
+
if (incompleteCheck.hasIncompleteWork) {
|
|
2056
|
+
// Agent has uncommitted/unpushed changes — block promotion
|
|
2057
|
+
log?.error('Code-producing agent completed without PR and has incomplete work — blocking promotion', {
|
|
2058
|
+
workType,
|
|
2059
|
+
reason: incompleteCheck.reason,
|
|
2060
|
+
details: incompleteCheck.details,
|
|
2061
|
+
});
|
|
2062
|
+
// Post a diagnostic comment
|
|
2063
|
+
try {
|
|
2064
|
+
await this.client.createComment(issueId, `⚠️ **Agent completed but work was not persisted.**\n\n` +
|
|
2065
|
+
`The agent reported success but no PR was detected, and the worktree has ${incompleteCheck.details}.\n\n` +
|
|
2066
|
+
`**Issue status was NOT promoted** to prevent lost work from advancing through the pipeline.\n\n` +
|
|
2067
|
+
`The worktree has been preserved at \`${agent.worktreePath}\`. ` +
|
|
2068
|
+
`To recover: cd into the worktree, commit, push, and create a PR manually.`);
|
|
2069
|
+
}
|
|
2070
|
+
catch {
|
|
2071
|
+
// Best-effort comment
|
|
2072
|
+
}
|
|
2073
|
+
// Do NOT set targetStatus — leave issue in current state
|
|
2074
|
+
}
|
|
2075
|
+
else {
|
|
2076
|
+
// No PR but worktree is clean — either no changes needed or agent cleaned up
|
|
2077
|
+
targetStatus = this.statusMappings.workTypeCompleteStatus[workType];
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
else {
|
|
2081
|
+
targetStatus = this.statusMappings.workTypeCompleteStatus[workType];
|
|
2082
|
+
}
|
|
2014
2083
|
}
|
|
2015
2084
|
if (targetStatus) {
|
|
2016
2085
|
try {
|
|
@@ -2455,7 +2524,7 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
|
|
|
2455
2524
|
}
|
|
2456
2525
|
}
|
|
2457
2526
|
else {
|
|
2458
|
-
// Direct
|
|
2527
|
+
// Direct issue tracker API - use session if available
|
|
2459
2528
|
const session = this.agentSessions.get(agent.issueId);
|
|
2460
2529
|
if (session) {
|
|
2461
2530
|
try {
|
|
@@ -2476,7 +2545,7 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
|
|
|
2476
2545
|
*/
|
|
2477
2546
|
async postCompletionComment(issueId, sessionId, resultMessage, log) {
|
|
2478
2547
|
// Build completion comments with multi-part splitting
|
|
2479
|
-
const comments = buildCompletionComments(resultMessage, [], // No plan items to include (already shown via activities)
|
|
2548
|
+
const comments = this.client.buildCompletionComments(resultMessage, [], // No plan items to include (already shown via activities)
|
|
2480
2549
|
sessionId ?? null);
|
|
2481
2550
|
log?.info('Posting completion comment', {
|
|
2482
2551
|
parts: comments.length,
|
|
@@ -2591,7 +2660,7 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
|
|
|
2591
2660
|
const { worktreePath, worktreeIdentifier } = this.createWorktree(issue.identifier, workType);
|
|
2592
2661
|
// Link dependencies from main repo into worktree
|
|
2593
2662
|
this.linkDependencies(worktreePath, issue.identifier);
|
|
2594
|
-
const startStatus =
|
|
2663
|
+
const startStatus = this.statusMappings.workTypeStartStatus[workType];
|
|
2595
2664
|
// Update issue status based on work type if auto-transition is enabled
|
|
2596
2665
|
if (this.config.autoTransition && startStatus) {
|
|
2597
2666
|
await this.client.updateIssueStatus(issue.id, startStatus);
|
|
@@ -2638,41 +2707,53 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
|
|
|
2638
2707
|
const issue = await this.client.getIssue(issueIdOrIdentifier);
|
|
2639
2708
|
const identifier = issue.identifier;
|
|
2640
2709
|
const issueId = issue.id; // Use the actual UUID
|
|
2641
|
-
const
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
const issueLabels = await issue.labels();
|
|
2645
|
-
const labelNames = issueLabels.nodes.map((l) => l.name);
|
|
2710
|
+
const teamName = issue.teamName;
|
|
2711
|
+
// Labels for provider resolution (pre-resolved by IssueTrackerClient)
|
|
2712
|
+
const labelNames = issue.labels;
|
|
2646
2713
|
// Resolve project name for path scoping in monorepos
|
|
2647
2714
|
let projectName;
|
|
2648
2715
|
if (this.projectPaths) {
|
|
2649
|
-
|
|
2650
|
-
projectName = project?.name;
|
|
2716
|
+
projectName = issue.projectName;
|
|
2651
2717
|
}
|
|
2652
2718
|
console.log(`Processing single issue: ${identifier} (${issueId}) - ${issue.title}`);
|
|
2653
2719
|
// Guard: skip work if the issue has moved to a terminal status since being queued
|
|
2654
|
-
const
|
|
2655
|
-
|
|
2656
|
-
if (currentStatus && TERMINAL_STATUSES.includes(currentStatus)) {
|
|
2720
|
+
const currentStatus = issue.status;
|
|
2721
|
+
if (currentStatus && this.statusMappings.terminalStatuses.includes(currentStatus)) {
|
|
2657
2722
|
throw new Error(`Issue ${identifier} is in terminal status '${currentStatus}' — skipping ${workType ?? 'auto'} work. ` +
|
|
2658
2723
|
`The issue was likely accepted/canceled after being queued.`);
|
|
2659
2724
|
}
|
|
2660
2725
|
// Defense in depth: re-validate git remote before spawning (guards against long-running instances)
|
|
2661
2726
|
if (this.config.repository) {
|
|
2662
|
-
validateGitRemote(this.config.repository);
|
|
2727
|
+
validateGitRemote(this.config.repository, this.gitRoot);
|
|
2663
2728
|
}
|
|
2664
2729
|
// Auto-detect work type from issue status if not provided
|
|
2665
2730
|
// This must happen BEFORE creating worktree since path includes work type suffix
|
|
2666
2731
|
let effectiveWorkType = workType;
|
|
2667
2732
|
if (!effectiveWorkType) {
|
|
2668
|
-
const
|
|
2669
|
-
const statusName = state?.name ?? 'Backlog';
|
|
2733
|
+
const statusName = issue.status ?? 'Backlog';
|
|
2670
2734
|
effectiveWorkType = await this.detectWorkType(issueId, statusName);
|
|
2671
2735
|
}
|
|
2736
|
+
else {
|
|
2737
|
+
// Re-validate: upgrade to coordination variant if this is a parent issue
|
|
2738
|
+
// The caller may have a stale work type from before the session was queued
|
|
2739
|
+
try {
|
|
2740
|
+
const isParent = await this.client.isParentIssue(issueId);
|
|
2741
|
+
if (isParent) {
|
|
2742
|
+
const upgraded = detectWorkType(issue.status ?? 'Backlog', isParent, this.statusMappings.statusToWorkType);
|
|
2743
|
+
if (upgraded !== effectiveWorkType) {
|
|
2744
|
+
console.log(`Upgrading work type from ${effectiveWorkType} to ${upgraded} (parent issue detected)`);
|
|
2745
|
+
effectiveWorkType = upgraded;
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
catch (err) {
|
|
2750
|
+
console.warn(`Failed to check parent status for coordination upgrade:`, err);
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2672
2753
|
// Create isolated worktree for the agent
|
|
2673
2754
|
let worktreePath;
|
|
2674
2755
|
let worktreeIdentifier;
|
|
2675
|
-
if (
|
|
2756
|
+
if (this.statusMappings.workTypesRequiringWorktree.has(effectiveWorkType)) {
|
|
2676
2757
|
const wt = this.createWorktree(identifier, effectiveWorkType);
|
|
2677
2758
|
worktreePath = wt.worktreePath;
|
|
2678
2759
|
worktreeIdentifier = wt.worktreeIdentifier;
|
|
@@ -2704,7 +2785,7 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
|
|
|
2704
2785
|
const effectiveSessionId = sessionId ?? recoveryCheck.state.linearSessionId ?? randomUUID();
|
|
2705
2786
|
console.log(`Resuming work on ${identifier} (recovery attempt ${updatedState?.recoveryAttempts ?? 1})`);
|
|
2706
2787
|
// Update status based on work type if auto-transition is enabled
|
|
2707
|
-
const startStatus =
|
|
2788
|
+
const startStatus = this.statusMappings.workTypeStartStatus[recoveryWorkType];
|
|
2708
2789
|
if (this.config.autoTransition && startStatus) {
|
|
2709
2790
|
await this.client.updateIssueStatus(issueId, startStatus);
|
|
2710
2791
|
console.log(`Updated ${identifier} status to ${startStatus}`);
|
|
@@ -2727,7 +2808,7 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
|
|
|
2727
2808
|
}
|
|
2728
2809
|
// No recovery needed - proceed with fresh spawn
|
|
2729
2810
|
// Update status based on work type if auto-transition is enabled
|
|
2730
|
-
const startStatus =
|
|
2811
|
+
const startStatus = this.statusMappings.workTypeStartStatus[effectiveWorkType];
|
|
2731
2812
|
if (this.config.autoTransition && startStatus) {
|
|
2732
2813
|
await this.client.updateIssueStatus(issueId, startStatus);
|
|
2733
2814
|
console.log(`Updated ${identifier} status to ${startStatus}`);
|
|
@@ -2900,12 +2981,10 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
|
|
|
2900
2981
|
try {
|
|
2901
2982
|
const issue = await this.client.getIssue(issueId);
|
|
2902
2983
|
identifier = issue.identifier;
|
|
2903
|
-
|
|
2904
|
-
teamName = issueTeam?.key;
|
|
2984
|
+
teamName = issue.teamName;
|
|
2905
2985
|
// Guard: skip work if the issue has moved to a terminal status since being queued
|
|
2906
|
-
const
|
|
2907
|
-
|
|
2908
|
-
if (currentStatus && TERMINAL_STATUSES.includes(currentStatus)) {
|
|
2986
|
+
const currentStatus = issue.status;
|
|
2987
|
+
if (currentStatus && this.statusMappings.terminalStatuses.includes(currentStatus)) {
|
|
2909
2988
|
console.log(`Issue ${identifier} is in terminal status '${currentStatus}' — skipping work`);
|
|
2910
2989
|
return {
|
|
2911
2990
|
forwarded: false,
|
|
@@ -2921,7 +3000,7 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
|
|
|
2921
3000
|
workType = await this.detectWorkType(issue.id, statusName);
|
|
2922
3001
|
}
|
|
2923
3002
|
// Create isolated worktree for the agent
|
|
2924
|
-
if (
|
|
3003
|
+
if (this.statusMappings.workTypesRequiringWorktree.has(workType)) {
|
|
2925
3004
|
const result = this.createWorktree(identifier, workType);
|
|
2926
3005
|
worktreePath = result.worktreePath;
|
|
2927
3006
|
worktreeIdentifier = result.worktreeIdentifier;
|
|
@@ -2940,7 +3019,7 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
|
|
|
2940
3019
|
}
|
|
2941
3020
|
// Check if worktree exists (only relevant for code work types)
|
|
2942
3021
|
const effectiveWorkType = workType ?? 'development';
|
|
2943
|
-
if (
|
|
3022
|
+
if (this.statusMappings.workTypesRequiringWorktree.has(effectiveWorkType) && worktreePath && !existsSync(worktreePath)) {
|
|
2944
3023
|
try {
|
|
2945
3024
|
const result = this.createWorktree(identifier, effectiveWorkType);
|
|
2946
3025
|
worktreePath = result.worktreePath;
|
|
@@ -3056,7 +3135,7 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
|
|
|
3056
3135
|
// Use the work type to determine if we need to transition on start
|
|
3057
3136
|
// Only certain work types trigger a start transition
|
|
3058
3137
|
const effectiveWorkType = workType ?? 'development';
|
|
3059
|
-
const startStatus =
|
|
3138
|
+
const startStatus = this.statusMappings.workTypeStartStatus[effectiveWorkType];
|
|
3060
3139
|
if (this.config.autoTransition && startStatus) {
|
|
3061
3140
|
try {
|
|
3062
3141
|
await this.client.updateIssueStatus(issueId, startStatus);
|
|
@@ -3168,9 +3247,8 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
|
|
|
3168
3247
|
});
|
|
3169
3248
|
}
|
|
3170
3249
|
else {
|
|
3171
|
-
// Direct
|
|
3172
|
-
const session =
|
|
3173
|
-
client: this.client.linearClient,
|
|
3250
|
+
// Direct issue tracker API
|
|
3251
|
+
const session = this.client.createSession({
|
|
3174
3252
|
issueId,
|
|
3175
3253
|
sessionId,
|
|
3176
3254
|
autoTransition: false,
|