opencode-rules-md 0.6.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.
- package/LICENSE +21 -0
- package/README.md +650 -0
- package/dist/cli.mjs +374 -0
- package/dist/src/active-rules-state.d.ts +12 -0
- package/dist/src/active-rules-state.d.ts.map +1 -0
- package/dist/src/active-rules-state.js +118 -0
- package/dist/src/active-rules-state.js.map +1 -0
- package/dist/src/cli/config.d.ts +62 -0
- package/dist/src/cli/config.d.ts.map +1 -0
- package/dist/src/cli/config.js +246 -0
- package/dist/src/cli/config.js.map +1 -0
- package/dist/src/cli/install.d.ts +21 -0
- package/dist/src/cli/install.d.ts.map +1 -0
- package/dist/src/cli/install.js +63 -0
- package/dist/src/cli/install.js.map +1 -0
- package/dist/src/cli/main.d.ts +19 -0
- package/dist/src/cli/main.d.ts.map +1 -0
- package/dist/src/cli/main.js +135 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/cli/real-fs.d.ts +26 -0
- package/dist/src/cli/real-fs.d.ts.map +1 -0
- package/dist/src/cli/real-fs.js +42 -0
- package/dist/src/cli/real-fs.js.map +1 -0
- package/dist/src/cli/status.d.ts +19 -0
- package/dist/src/cli/status.d.ts.map +1 -0
- package/dist/src/cli/status.js +58 -0
- package/dist/src/cli/status.js.map +1 -0
- package/dist/src/debug.d.ts +3 -0
- package/dist/src/debug.d.ts.map +1 -0
- package/dist/src/debug.js +8 -0
- package/dist/src/debug.js.map +1 -0
- package/dist/src/git-branch.d.ts +2 -0
- package/dist/src/git-branch.d.ts.map +1 -0
- package/dist/src/git-branch.js +30 -0
- package/dist/src/git-branch.js.map +1 -0
- package/dist/src/index.d.ts +26 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +55 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/mcp-tools.d.ts +8 -0
- package/dist/src/mcp-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools.js +18 -0
- package/dist/src/mcp-tools.js.map +1 -0
- package/dist/src/message-context.d.ts +55 -0
- package/dist/src/message-context.d.ts.map +1 -0
- package/dist/src/message-context.js +110 -0
- package/dist/src/message-context.js.map +1 -0
- package/dist/src/message-paths.d.ts +34 -0
- package/dist/src/message-paths.d.ts.map +1 -0
- package/dist/src/message-paths.js +112 -0
- package/dist/src/message-paths.js.map +1 -0
- package/dist/src/project-fingerprint.d.ts +2 -0
- package/dist/src/project-fingerprint.d.ts.map +1 -0
- package/dist/src/project-fingerprint.js +61 -0
- package/dist/src/project-fingerprint.js.map +1 -0
- package/dist/src/rule-discovery.d.ts +49 -0
- package/dist/src/rule-discovery.d.ts.map +1 -0
- package/dist/src/rule-discovery.js +143 -0
- package/dist/src/rule-discovery.js.map +1 -0
- package/dist/src/rule-filter.d.ts +61 -0
- package/dist/src/rule-filter.d.ts.map +1 -0
- package/dist/src/rule-filter.js +180 -0
- package/dist/src/rule-filter.js.map +1 -0
- package/dist/src/rule-metadata.d.ts +29 -0
- package/dist/src/rule-metadata.d.ts.map +1 -0
- package/dist/src/rule-metadata.js +100 -0
- package/dist/src/rule-metadata.js.map +1 -0
- package/dist/src/runtime-chat.d.ts +34 -0
- package/dist/src/runtime-chat.d.ts.map +1 -0
- package/dist/src/runtime-chat.js +39 -0
- package/dist/src/runtime-chat.js.map +1 -0
- package/dist/src/runtime-context.d.ts +25 -0
- package/dist/src/runtime-context.d.ts.map +1 -0
- package/dist/src/runtime-context.js +109 -0
- package/dist/src/runtime-context.js.map +1 -0
- package/dist/src/runtime.d.ts +31 -0
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/runtime.js +238 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/session-store.d.ts +34 -0
- package/dist/src/session-store.d.ts.map +1 -0
- package/dist/src/session-store.js +90 -0
- package/dist/src/session-store.js.map +1 -0
- package/dist/src/utils.d.ts +15 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +19 -0
- package/dist/src/utils.js.map +1 -0
- package/dist/tui/data/rules.d.ts +63 -0
- package/dist/tui/data/rules.d.ts.map +1 -0
- package/dist/tui/data/rules.js +173 -0
- package/dist/tui/data/rules.js.map +1 -0
- package/dist/tui/index.d.ts +8 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +13 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/slots/sidebar-content.d.ts +11 -0
- package/dist/tui/slots/sidebar-content.d.ts.map +1 -0
- package/dist/tui/slots/sidebar-content.js +150 -0
- package/dist/tui/slots/sidebar-content.js.map +1 -0
- package/package.json +101 -0
- package/src/active-rules-state.ts +166 -0
- package/src/cli/config.ts +312 -0
- package/src/cli/install.ts +94 -0
- package/src/cli/main.ts +167 -0
- package/src/cli/real-fs.ts +65 -0
- package/src/cli/status.ts +80 -0
- package/src/debug.ts +9 -0
- package/src/git-branch.ts +37 -0
- package/src/index.ts +68 -0
- package/src/mcp-tools.ts +24 -0
- package/src/message-context.ts +139 -0
- package/src/message-paths.ts +153 -0
- package/src/project-fingerprint.ts +72 -0
- package/src/rule-discovery.ts +205 -0
- package/src/rule-filter.ts +269 -0
- package/src/rule-metadata.ts +161 -0
- package/src/runtime-chat.ts +68 -0
- package/src/runtime-context.ts +136 -0
- package/src/runtime.ts +369 -0
- package/src/session-store.ts +129 -0
- package/src/utils.ts +36 -0
- package/tui/.atl/.skill-registry.cache.json +3 -0
- package/tui/.atl/skill-registry.md +57 -0
- package/tui/data/rules.ts +230 -0
- package/tui/index.tsx +23 -0
- package/tui/slots/sidebar-content.tsx +364 -0
- package/tui/types/opencode-plugin-tui.d.ts +78 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule metadata parsing and frontmatter extraction
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { parse: parseYaml } = await import('yaml');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Metadata extracted from .mdc file frontmatter
|
|
9
|
+
*/
|
|
10
|
+
export interface RuleMetadata {
|
|
11
|
+
globs?: string[];
|
|
12
|
+
keywords?: string[];
|
|
13
|
+
tools?: string[];
|
|
14
|
+
model?: string[];
|
|
15
|
+
agent?: string[];
|
|
16
|
+
command?: string[];
|
|
17
|
+
project?: string[];
|
|
18
|
+
branch?: string[];
|
|
19
|
+
os?: string[];
|
|
20
|
+
ci?: boolean;
|
|
21
|
+
match?: 'any' | 'all';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Raw parsed YAML frontmatter structure
|
|
26
|
+
*/
|
|
27
|
+
interface ParsedFrontmatter {
|
|
28
|
+
globs?: unknown;
|
|
29
|
+
keywords?: unknown;
|
|
30
|
+
tools?: unknown;
|
|
31
|
+
model?: unknown;
|
|
32
|
+
agent?: unknown;
|
|
33
|
+
command?: unknown;
|
|
34
|
+
project?: unknown;
|
|
35
|
+
branch?: unknown;
|
|
36
|
+
os?: unknown;
|
|
37
|
+
ci?: unknown;
|
|
38
|
+
match?: unknown;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Field names in ParsedFrontmatter that are string arrays */
|
|
42
|
+
type StringArrayField =
|
|
43
|
+
| 'globs'
|
|
44
|
+
| 'keywords'
|
|
45
|
+
| 'tools'
|
|
46
|
+
| 'model'
|
|
47
|
+
| 'agent'
|
|
48
|
+
| 'command'
|
|
49
|
+
| 'project'
|
|
50
|
+
| 'branch'
|
|
51
|
+
| 'os';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Extract and normalize a string array from parsed frontmatter.
|
|
55
|
+
* Filters non-strings, trims whitespace, and removes empty values.
|
|
56
|
+
*
|
|
57
|
+
* @param value - Raw value from parsed YAML (may be array or undefined)
|
|
58
|
+
* @returns Normalized string array, or undefined if empty after filtering
|
|
59
|
+
*/
|
|
60
|
+
function extractStringArray(value: unknown): string[] | undefined {
|
|
61
|
+
if (!Array.isArray(value)) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
const result = value
|
|
65
|
+
.filter((v): v is string => typeof v === 'string')
|
|
66
|
+
.map(v => v.trim())
|
|
67
|
+
.filter(v => v.length > 0);
|
|
68
|
+
return result.length > 0 ? result : undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Parse YAML metadata from rule file content using the yaml package.
|
|
73
|
+
* Extracts frontmatter (---) and returns metadata object.
|
|
74
|
+
*/
|
|
75
|
+
export function parseRuleMetadata(content: string): RuleMetadata | undefined {
|
|
76
|
+
// Check if content starts with frontmatter
|
|
77
|
+
if (!content.startsWith('---')) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Find the closing --- marker
|
|
82
|
+
const endIndex = content.indexOf('---', 3);
|
|
83
|
+
if (endIndex === -1) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Extract the YAML frontmatter
|
|
88
|
+
const frontmatter = content.substring(3, endIndex).trim();
|
|
89
|
+
if (!frontmatter) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
// Parse YAML using the yaml package
|
|
95
|
+
const parsed = parseYaml(frontmatter) as ParsedFrontmatter | null;
|
|
96
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const metadata: RuleMetadata = {};
|
|
101
|
+
|
|
102
|
+
// Array fields to extract using shared helper
|
|
103
|
+
const arrayFields: StringArrayField[] = [
|
|
104
|
+
'globs',
|
|
105
|
+
'keywords',
|
|
106
|
+
'tools',
|
|
107
|
+
'model',
|
|
108
|
+
'agent',
|
|
109
|
+
'command',
|
|
110
|
+
'project',
|
|
111
|
+
'branch',
|
|
112
|
+
'os',
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
for (const field of arrayFields) {
|
|
116
|
+
const extracted = extractStringArray(parsed[field]);
|
|
117
|
+
if (extracted) {
|
|
118
|
+
metadata[field] = extracted;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Extract ci boolean (only if strictly boolean)
|
|
123
|
+
if (typeof parsed.ci === 'boolean') {
|
|
124
|
+
metadata.ci = parsed.ci;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Extract match (normalize to 'any' | 'all' only)
|
|
128
|
+
if (parsed.match === 'any' || parsed.match === 'all') {
|
|
129
|
+
metadata.match = parsed.match;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Return metadata only if it has content
|
|
133
|
+
return Object.keys(metadata).length > 0 ? metadata : undefined;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
// Log warning for YAML parsing errors
|
|
136
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
137
|
+
console.warn(
|
|
138
|
+
`[opencode-rules-md] Warning: Failed to parse YAML frontmatter: ${message}`
|
|
139
|
+
);
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Strip YAML frontmatter from rule content
|
|
146
|
+
*/
|
|
147
|
+
export function stripFrontmatter(content: string): string {
|
|
148
|
+
// Check if content starts with frontmatter
|
|
149
|
+
if (!content.startsWith('---')) {
|
|
150
|
+
return content;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Find the closing --- marker
|
|
154
|
+
const endIndex = content.indexOf('---', 3);
|
|
155
|
+
if (endIndex === -1) {
|
|
156
|
+
return content;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Return content after the closing marker, trimming leading newline
|
|
160
|
+
return content.substring(endIndex + 3).trimStart();
|
|
161
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { extractTextFromParts } from './message-context.js';
|
|
2
|
+
import type { SessionStore } from './session-store.js';
|
|
3
|
+
import type { DebugLog } from './debug.js';
|
|
4
|
+
|
|
5
|
+
export interface ChatMessageInput {
|
|
6
|
+
sessionID?: string;
|
|
7
|
+
model?: { modelID?: string };
|
|
8
|
+
agent?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ChatMessageOutput {
|
|
12
|
+
message?: { role?: string };
|
|
13
|
+
parts?: Array<{ type?: string; text?: string; synthetic?: boolean }>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Extract user prompt text from chat message parts.
|
|
18
|
+
* Returns empty string if no text parts found.
|
|
19
|
+
*/
|
|
20
|
+
export function extractUserPromptFromParts(
|
|
21
|
+
parts:
|
|
22
|
+
| Array<{ type?: string; text?: string; synthetic?: boolean }>
|
|
23
|
+
| undefined
|
|
24
|
+
): string {
|
|
25
|
+
if (!parts) return '';
|
|
26
|
+
return extractTextFromParts(parts);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Handle incoming chat messages to update session state.
|
|
31
|
+
* Captures user prompts, model IDs, and agent types.
|
|
32
|
+
*/
|
|
33
|
+
export function handleChatMessage(
|
|
34
|
+
input: ChatMessageInput,
|
|
35
|
+
output: ChatMessageOutput,
|
|
36
|
+
sessionStore: SessionStore,
|
|
37
|
+
debugLog: DebugLog
|
|
38
|
+
): void {
|
|
39
|
+
const sessionID = input?.sessionID;
|
|
40
|
+
if (!sessionID) {
|
|
41
|
+
debugLog('No sessionID in chat.message hook input');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (output?.message?.role !== 'user') {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const userPrompt = extractUserPromptFromParts(output.parts);
|
|
50
|
+
|
|
51
|
+
sessionStore.upsert(sessionID, state => {
|
|
52
|
+
if (userPrompt) {
|
|
53
|
+
state.lastUserPrompt = userPrompt;
|
|
54
|
+
state.rulesInjected = false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (input.model?.modelID) {
|
|
58
|
+
state.lastModelID = input.model.modelID;
|
|
59
|
+
}
|
|
60
|
+
if (input.agent) {
|
|
61
|
+
state.lastAgentType = input.agent;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
debugLog(
|
|
66
|
+
`Updated session ${sessionID} from chat.message (model=${input.model?.modelID ?? 'none'}, agent=${input.agent ?? 'none'})`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { extractSlashCommand } from './message-context.js';
|
|
2
|
+
import { detectProjectTags } from './project-fingerprint.js';
|
|
3
|
+
import { getGitBranch } from './git-branch.js';
|
|
4
|
+
import type { RuleFilterContext } from './rule-filter.js';
|
|
5
|
+
import type { DebugLog } from './debug.js';
|
|
6
|
+
|
|
7
|
+
export interface BuildFilterContextOptions {
|
|
8
|
+
contextFilePaths: string[];
|
|
9
|
+
userPrompt: string | undefined;
|
|
10
|
+
availableToolIDs: string[];
|
|
11
|
+
modelID: string | undefined;
|
|
12
|
+
agentType: string | undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Parse an env variable value semantically: 'false', '0', '' => false; other non-empty => true.
|
|
17
|
+
* Returns undefined if the variable is not set.
|
|
18
|
+
*/
|
|
19
|
+
function parseEnvBoolean(value: string | undefined): boolean | undefined {
|
|
20
|
+
if (value === undefined) return undefined;
|
|
21
|
+
if (value === '') return false;
|
|
22
|
+
const lower = value.toLowerCase();
|
|
23
|
+
if (lower === 'false' || lower === '0') return false;
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if a string value represents a truthy CI environment variable.
|
|
29
|
+
* Treats 'false', '0', and empty strings as falsy; other non-empty values as truthy.
|
|
30
|
+
*/
|
|
31
|
+
function isTruthyEnvValue(value: string | undefined): boolean {
|
|
32
|
+
return parseEnvBoolean(value) === true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Detect if running in a CI environment by checking common CI environment variables.
|
|
37
|
+
*
|
|
38
|
+
* If process.env.CI is explicitly set, it is treated as authoritative:
|
|
39
|
+
* - CI='false' or CI='0' or CI='' => return false (no provider var fallback)
|
|
40
|
+
* - CI='true' or CI='1' or any truthy value => return true
|
|
41
|
+
*
|
|
42
|
+
* If process.env.CI is not set (undefined), fall back to provider-specific detection.
|
|
43
|
+
*/
|
|
44
|
+
export function detectCiEnvironment(): boolean {
|
|
45
|
+
const env = process.env;
|
|
46
|
+
|
|
47
|
+
const ciExplicit = parseEnvBoolean(env.CI);
|
|
48
|
+
if (ciExplicit !== undefined) {
|
|
49
|
+
return ciExplicit;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
isTruthyEnvValue(env.CONTINUOUS_INTEGRATION) ||
|
|
54
|
+
isTruthyEnvValue(env.BUILD_NUMBER) ||
|
|
55
|
+
isTruthyEnvValue(env.GITHUB_ACTIONS) ||
|
|
56
|
+
isTruthyEnvValue(env.GITLAB_CI) ||
|
|
57
|
+
isTruthyEnvValue(env.CIRCLECI) ||
|
|
58
|
+
isTruthyEnvValue(env.TRAVIS) ||
|
|
59
|
+
isTruthyEnvValue(env.JENKINS_URL) ||
|
|
60
|
+
isTruthyEnvValue(env.BUILDKITE) ||
|
|
61
|
+
isTruthyEnvValue(env.TEAMCITY_VERSION)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Build the filter context object used for rule matching.
|
|
67
|
+
* Assembles runtime information from various sources.
|
|
68
|
+
*/
|
|
69
|
+
export async function buildFilterContext(
|
|
70
|
+
opts: BuildFilterContextOptions,
|
|
71
|
+
projectDirectory: string,
|
|
72
|
+
debugLog: DebugLog
|
|
73
|
+
): Promise<RuleFilterContext> {
|
|
74
|
+
const { contextFilePaths, userPrompt, availableToolIDs, modelID, agentType } =
|
|
75
|
+
opts;
|
|
76
|
+
|
|
77
|
+
const command = extractSlashCommand(userPrompt);
|
|
78
|
+
|
|
79
|
+
let projectTags: string[] | undefined;
|
|
80
|
+
try {
|
|
81
|
+
projectTags = await detectProjectTags(projectDirectory);
|
|
82
|
+
if (projectTags.length === 0) {
|
|
83
|
+
projectTags = undefined;
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
projectTags = undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let gitBranch: string | undefined;
|
|
90
|
+
try {
|
|
91
|
+
gitBranch = await getGitBranch(projectDirectory);
|
|
92
|
+
} catch {
|
|
93
|
+
gitBranch = undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const os = process.platform;
|
|
97
|
+
const ci = detectCiEnvironment();
|
|
98
|
+
|
|
99
|
+
const context: RuleFilterContext = {
|
|
100
|
+
os,
|
|
101
|
+
ci,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
if (contextFilePaths.length > 0) {
|
|
105
|
+
context.contextFilePaths = contextFilePaths;
|
|
106
|
+
}
|
|
107
|
+
if (userPrompt !== undefined) {
|
|
108
|
+
context.userPrompt = userPrompt;
|
|
109
|
+
}
|
|
110
|
+
if (availableToolIDs.length > 0) {
|
|
111
|
+
context.availableToolIDs = availableToolIDs;
|
|
112
|
+
}
|
|
113
|
+
if (modelID !== undefined) {
|
|
114
|
+
context.modelID = modelID;
|
|
115
|
+
}
|
|
116
|
+
if (agentType !== undefined) {
|
|
117
|
+
context.agentType = agentType;
|
|
118
|
+
}
|
|
119
|
+
if (command !== undefined) {
|
|
120
|
+
context.command = command;
|
|
121
|
+
}
|
|
122
|
+
if (projectTags !== undefined) {
|
|
123
|
+
context.projectTags = projectTags;
|
|
124
|
+
}
|
|
125
|
+
if (gitBranch !== undefined) {
|
|
126
|
+
context.gitBranch = gitBranch;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
debugLog(
|
|
130
|
+
`Filter context: model=${modelID ?? 'none'}, agent=${agentType ?? 'none'}, ` +
|
|
131
|
+
`command=${command ?? 'none'}, branch=${gitBranch ?? 'none'}, ` +
|
|
132
|
+
`os=${os}, ci=${ci}, projectTags=${projectTags?.join(',') ?? 'none'}`
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
return context;
|
|
136
|
+
}
|