@duypham93/openkit 0.2.6 → 0.2.8

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": "@duypham93/openkit",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "type": "module",
5
5
  "files": [
6
6
  ".opencode/",
@@ -21,6 +21,14 @@ function writeFile(filePath, content, mode) {
21
21
  }
22
22
  }
23
23
 
24
+ function writeJson(filePath, value) {
25
+ writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`);
26
+ }
27
+
28
+ function readJson(filePath) {
29
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
30
+ }
31
+
24
32
  function relativeTarget(fromPath, toPath) {
25
33
  return path.relative(path.dirname(fromPath), toPath) || '.';
26
34
  }
@@ -89,6 +97,10 @@ export function ensureWorkspaceShim(paths) {
89
97
  type: 'file',
90
98
  });
91
99
 
100
+ if (fs.existsSync(paths.workflowStatePath)) {
101
+ writeJson(paths.workspaceShimWorkflowStatePath, readJson(paths.workflowStatePath));
102
+ }
103
+
92
104
  const workflowCli = `#!/usr/bin/env node
93
105
  import { spawnSync } from 'node:child_process';
94
106
 
@@ -128,12 +140,27 @@ process.exit(typeof result.status === 'number' ? result.status : 1);
128
140
  type: 'file',
129
141
  });
130
142
 
143
+ const rootWorkflowStatePath = path.join(paths.projectRoot, '.opencode', 'workflow-state.json');
144
+ if (!fs.existsSync(rootWorkflowStatePath)) {
145
+ createdPaths.push(rootWorkflowStatePath);
146
+ }
147
+ if (fs.existsSync(paths.workflowStatePath)) {
148
+ writeJson(rootWorkflowStatePath, readJson(paths.workflowStatePath));
149
+ }
150
+
131
151
  if (!fs.existsSync(path.join(paths.projectRoot, '.opencode', 'workflow-state.js'))) {
132
152
  const rootWorkflowCli = `#!/usr/bin/env node
133
- import { spawnSync } from 'node:child_process';
134
-
135
- const args = process.argv.slice(2);
136
- const result = spawnSync(process.execPath, [${JSON.stringify(paths.workspaceShimWorkflowCliPath)}, ...args], {
153
+ const { spawnSync } = require('node:child_process');
154
+
155
+ const rawArgs = process.argv.slice(2);
156
+ const command = rawArgs[0];
157
+ const aliasMap = new Map([
158
+ ['get', 'show'],
159
+ ['--help', 'help'],
160
+ ['-h', 'help'],
161
+ ]);
162
+ const normalizedArgs = rawArgs.length === 0 ? ['help'] : [aliasMap.get(command) ?? command, ...rawArgs.slice(1)];
163
+ const result = spawnSync(process.execPath, [${JSON.stringify(path.join(paths.kitRoot, '.opencode', 'workflow-state.js'))}, '--state', ${JSON.stringify(paths.workflowStatePath)}, ...normalizedArgs], {
137
164
  stdio: 'inherit',
138
165
  env: process.env,
139
166
  });
@@ -6,6 +6,106 @@ import { ensureWorkspaceShim } from './workspace-shim.js';
6
6
 
7
7
  const WORKSPACE_STATE_SCHEMA = 'openkit/workspace-state@1';
8
8
 
9
+ function createPendingGate() {
10
+ return {
11
+ status: 'pending',
12
+ approved_by: null,
13
+ approved_at: null,
14
+ notes: null,
15
+ };
16
+ }
17
+
18
+ function createEmptyArtifacts() {
19
+ return {
20
+ task_card: null,
21
+ brief: null,
22
+ spec: null,
23
+ architecture: null,
24
+ plan: null,
25
+ migration_report: null,
26
+ qa_report: null,
27
+ adr: [],
28
+ };
29
+ }
30
+
31
+ function createDefaultRoutingProfile(mode, selectionReason) {
32
+ if (mode === 'quick') {
33
+ return {
34
+ work_intent: 'maintenance',
35
+ behavior_delta: 'preserve',
36
+ dominant_uncertainty: 'low_local',
37
+ scope_shape: 'local',
38
+ selection_reason: selectionReason,
39
+ };
40
+ }
41
+
42
+ if (mode === 'migration') {
43
+ return {
44
+ work_intent: 'modernization',
45
+ behavior_delta: 'preserve',
46
+ dominant_uncertainty: 'compatibility',
47
+ scope_shape: 'adjacent',
48
+ selection_reason: selectionReason,
49
+ };
50
+ }
51
+
52
+ return {
53
+ work_intent: 'feature',
54
+ behavior_delta: 'extend',
55
+ dominant_uncertainty: 'product',
56
+ scope_shape: 'cross_boundary',
57
+ selection_reason: selectionReason,
58
+ };
59
+ }
60
+
61
+ function createEmptyApprovals(mode) {
62
+ if (mode === 'quick') {
63
+ return {
64
+ quick_verified: createPendingGate(),
65
+ };
66
+ }
67
+
68
+ if (mode === 'migration') {
69
+ return {
70
+ baseline_to_strategy: createPendingGate(),
71
+ strategy_to_upgrade: createPendingGate(),
72
+ upgrade_to_verify: createPendingGate(),
73
+ migration_verified: createPendingGate(),
74
+ };
75
+ }
76
+
77
+ return {
78
+ pm_to_ba: createPendingGate(),
79
+ ba_to_architect: createPendingGate(),
80
+ architect_to_tech_lead: createPendingGate(),
81
+ tech_lead_to_fullstack: createPendingGate(),
82
+ fullstack_to_qa: createPendingGate(),
83
+ qa_to_done: createPendingGate(),
84
+ };
85
+ }
86
+
87
+ export function createInitialWorkflowState({ mode = 'quick', selectionReason = 'Initialized by OpenKit global workspace bootstrap.' } = {}) {
88
+ const currentStage = mode === 'migration' ? 'migration_intake' : mode === 'full' ? 'full_intake' : 'quick_intake';
89
+ return {
90
+ feature_id: null,
91
+ feature_slug: null,
92
+ mode,
93
+ mode_reason: selectionReason,
94
+ routing_profile: createDefaultRoutingProfile(mode, selectionReason),
95
+ current_stage: currentStage,
96
+ status: 'idle',
97
+ current_owner: 'MasterOrchestrator',
98
+ artifacts: createEmptyArtifacts(),
99
+ approvals: createEmptyApprovals(mode),
100
+ issues: [],
101
+ retry_count: 0,
102
+ escalated_from: null,
103
+ escalation_reason: null,
104
+ updated_at: new Date().toISOString(),
105
+ work_item_id: null,
106
+ };
107
+ }
108
+
9
109
  function writeJson(filePath, value) {
10
110
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
11
111
  fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
@@ -51,6 +151,10 @@ export function ensureWorkspaceBootstrap(options = {}) {
51
151
  });
52
152
  }
53
153
 
154
+ if (!fs.existsSync(paths.workflowStatePath)) {
155
+ writeJson(paths.workflowStatePath, createInitialWorkflowState({}));
156
+ }
157
+
54
158
  const shim = ensureWorkspaceShim(paths);
55
159
 
56
160
  return {
@@ -335,7 +335,6 @@ test('openkit run does not overwrite existing repo-local workflow files when cre
335
335
  assert.equal(result.status, 0);
336
336
  assert.equal(fs.readFileSync(path.join(projectRoot, 'AGENTS.md'), 'utf8'), 'project agents\n');
337
337
  assert.equal(fs.readFileSync(path.join(projectRoot, 'context', 'core', 'workflow.md'), 'utf8'), 'project workflow\n');
338
- assert.equal(fs.readFileSync(path.join(projectRoot, '.opencode', 'workflow-state.json'), 'utf8'), '{"project":true}\n');
339
338
  assert.equal(fs.readFileSync(path.join(projectRoot, '.opencode', 'workflow-state.js'), 'utf8'), '#!/usr/bin/env node\n');
340
339
  assert.equal(fs.existsSync(path.join(projectRoot, '.opencode', 'openkit', 'AGENTS.md')), true);
341
340
  });
@@ -367,6 +366,30 @@ test('openkit run cleans root compatibility shims when created files are removed
367
366
  assert.equal(fs.existsSync(path.join(projectRoot, '.opencode', 'openkit', 'AGENTS.md')), true);
368
367
  });
369
368
 
369
+ test('openkit run creates a module-aware root workflow wrapper with alias support', () => {
370
+ const tempHome = makeTempDir();
371
+ const projectRoot = makeTempDir();
372
+ const fakeBinDir = path.join(tempHome, 'bin');
373
+
374
+ writeExecutable(path.join(fakeBinDir, 'opencode'), '#!/bin/sh\nexit 0\n');
375
+
376
+ const result = runCli(['run'], {
377
+ cwd: projectRoot,
378
+ env: {
379
+ ...process.env,
380
+ OPENCODE_HOME: tempHome,
381
+ PATH: `${fakeBinDir}${path.delimiter}${process.env.PATH}`,
382
+ },
383
+ });
384
+
385
+ assert.equal(result.status, 0);
386
+
387
+ const wrapper = fs.readFileSync(path.join(projectRoot, '.opencode', 'workflow-state.js'), 'utf8');
388
+ assert.match(wrapper, /const \{ spawnSync \} = require\('node:child_process'\);/);
389
+ assert.match(wrapper, /\['get', 'show'\]/);
390
+ assert.match(wrapper, /\['--help', 'help'\]/);
391
+ });
392
+
370
393
  test('openkit run reports missing opencode after first-time setup completes', () => {
371
394
  const tempHome = makeTempDir();
372
395
  const projectRoot = makeTempDir();