@fro.bot/systematic 2.12.0 → 2.12.1

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/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  findCommandsInDir,
7
7
  findSkillsInDir,
8
8
  getConfigPaths
9
- } from "./index-g602hys2.js";
9
+ } from "./index-b4ht76qd.js";
10
10
 
11
11
  // src/cli.ts
12
12
  import fs from "fs";
package/dist/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
1
  import type { Plugin } from '@opencode-ai/plugin';
2
+ export declare const applyBootstrapContent: (output: {
3
+ system: string[];
4
+ }, content: string) => void;
2
5
  declare const SystematicPlugin: Plugin;
3
6
  export default SystematicPlugin;
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  loadConfig,
14
14
  loadConfigWithSources,
15
15
  parseFrontmatter
16
- } from "./index-g602hys2.js";
16
+ } from "./index-b4ht76qd.js";
17
17
 
18
18
  // src/index.ts
19
19
  import fs4 from "fs";
@@ -686,29 +686,6 @@ function registerSkillsPaths(config, skillsDir) {
686
686
  };
687
687
  }
688
688
 
689
- // src/lib/plugin-singleton.ts
690
- var SINGLETON_KEY = Symbol.for("systematic.singleton.v1");
691
- async function plugInOnce({
692
- doInit,
693
- pid
694
- }) {
695
- const currentPid = pid ?? process.pid;
696
- const g = globalThis;
697
- const existing = g[SINGLETON_KEY];
698
- if (existing && existing.pid === currentPid) {
699
- const hooks2 = await existing.hooksPromise;
700
- return { isFirst: false, hooks: hooks2 };
701
- }
702
- const hooksPromise = doInit();
703
- g[SINGLETON_KEY] = {
704
- pid: currentPid,
705
- loadedAt: Date.now(),
706
- hooksPromise
707
- };
708
- const hooks = await hooksPromise;
709
- return { isFirst: true, hooks };
710
- }
711
-
712
689
  // src/lib/skill-tool.ts
713
690
  import fs3 from "fs";
714
691
  import path4 from "path";
@@ -871,8 +848,26 @@ var bundledSkillsDir = path5.join(packageRoot, "skills");
871
848
  var bundledAgentsDir = path5.join(packageRoot, "agents");
872
849
  var bundledCommandsDir = path5.join(packageRoot, "commands");
873
850
  var packageJsonPath = path5.join(packageRoot, "package.json");
874
- var hasLoggedInit = false;
851
+ var BOOTSTRAP_MARKER_OPEN = "<SYSTEMATIC_WORKFLOWS>";
852
+ var BOOTSTRAP_MARKER_CLOSE = "</SYSTEMATIC_WORKFLOWS>";
853
+ var findBootstrapMarkerBlock = (entry) => {
854
+ const start = entry.indexOf(BOOTSTRAP_MARKER_OPEN);
855
+ if (start === -1)
856
+ return null;
857
+ const closeStart = entry.indexOf(BOOTSTRAP_MARKER_CLOSE, start + BOOTSTRAP_MARKER_OPEN.length);
858
+ if (closeStart === -1)
859
+ return null;
860
+ return { start, end: closeStart + BOOTSTRAP_MARKER_CLOSE.length };
861
+ };
875
862
  var applyBootstrapContent = (output, content) => {
863
+ for (let i = 0;i < output.system.length; i++) {
864
+ const entry = output.system[i];
865
+ const block = findBootstrapMarkerBlock(entry);
866
+ if (block !== null) {
867
+ output.system[i] = entry.slice(0, block.start) + content + entry.slice(block.end);
868
+ return;
869
+ }
870
+ }
876
871
  if (output.system.length > 0) {
877
872
  output.system[output.system.length - 1] += `
878
873
 
@@ -893,6 +888,7 @@ var getPackageVersion = () => {
893
888
  }
894
889
  };
895
890
  var initializePlugin = async ({ client, directory }) => {
891
+ let hasLoggedInit = false;
896
892
  const config = loadConfig(directory);
897
893
  const bootstrapContent = getBootstrapContent(config, { bundledSkillsDir });
898
894
  const configHandler = createConfigHandler({
@@ -951,12 +947,10 @@ var initializePlugin = async ({ client, directory }) => {
951
947
  };
952
948
  };
953
949
  var SystematicPlugin = async (input) => {
954
- const { hooks } = await plugInOnce({
955
- doInit: () => initializePlugin(input)
956
- });
957
- return hooks;
950
+ return initializePlugin(input);
958
951
  };
959
952
  var src_default = SystematicPlugin;
960
953
  export {
961
- src_default as default
954
+ src_default as default,
955
+ applyBootstrapContent
962
956
  };
@@ -158,11 +158,13 @@ export declare const SystematicConfigSchema: z.ZodObject<{
158
158
  file: z.ZodOptional<z.ZodString>;
159
159
  }, z.core.$strict>>;
160
160
  }, z.core.$strict>;
161
- export interface ValidationResult {
162
- success: boolean;
163
- data?: z.infer<typeof SystematicConfigSchema>;
164
- errors?: readonly z.ZodIssue[];
165
- }
161
+ export type ValidationResult = {
162
+ success: true;
163
+ data: z.infer<typeof SystematicConfigSchema>;
164
+ } | {
165
+ success: false;
166
+ errors: readonly z.ZodIssue[];
167
+ };
166
168
  export declare function validateConfig(input: unknown): ValidationResult;
167
169
  export declare function assertSourceCategoryModelDefaults(defaults: Record<string, unknown>): void;
168
170
  /**
@@ -1,4 +1,3 @@
1
- import type { z } from 'zod';
2
1
  export interface BootstrapConfig {
3
2
  enabled: boolean;
4
3
  file?: string;
@@ -27,26 +26,6 @@ export interface SystematicConfig {
27
26
  categories?: OverlayConfigMap;
28
27
  }
29
28
  export declare const DEFAULT_CONFIG: SystematicConfig;
30
- interface RawSystematicConfig extends Omit<Partial<SystematicConfig>, 'agents' | 'categories'> {
31
- agents?: unknown;
32
- categories?: unknown;
33
- }
34
- interface ConfigSource {
35
- path: string;
36
- config: RawSystematicConfig;
37
- trust: 'user' | 'project' | 'custom';
38
- }
39
- /**
40
- * Type guard for errors thrown by the top-level config schema validator.
41
- * Use this to distinguish schema validation failures from JSONC parse errors
42
- * or file read errors.
43
- */
44
- export declare function isConfigSchemaError(err: unknown): err is Error & {
45
- _tag: 'ConfigSchemaError';
46
- filePath: string;
47
- trust: ConfigSource['trust'];
48
- issues: readonly z.core.$ZodIssue[];
49
- };
50
29
  export declare function loadConfig(projectDir: string): SystematicConfig;
51
30
  export declare function loadConfigWithSources(projectDir: string): SourceAwareConfigResult;
52
31
  export declare function getConfigPaths(projectDir: string): {
@@ -57,4 +36,3 @@ export declare function getConfigPaths(projectDir: string): {
57
36
  userDir: string;
58
37
  projectDir: string;
59
38
  };
60
- export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fro.bot/systematic",
3
- "version": "2.12.0",
3
+ "version": "2.12.1",
4
4
  "description": "Structured engineering workflows for OpenCode",
5
5
  "type": "module",
6
6
  "homepage": "https://fro.bot/systematic",
@@ -1,60 +0,0 @@
1
- /**
2
- * Per-process register-once guard for the Systematic plugin factory.
3
- *
4
- * OpenCode invokes the plugin factory more than once per process when the
5
- * same plugin is referenced by multiple config sources (for example a
6
- * user-level `~/.config/opencode/opencode.json` AND a project-level
7
- * `opencode.json`). Each invocation evaluates the plugin module fresh —
8
- * module-local variables reset between calls — so the guard state must
9
- * live on `globalThis` to persist across module instances within the
10
- * same process.
11
- *
12
- * On the first invocation `doInit` runs and the resulting hooks promise
13
- * is cached on `globalThis`; the caller receives `{ isFirst: true, hooks }`.
14
- * On every subsequent invocation in the same PID `doInit` is skipped and
15
- * the cached resolved hooks are returned directly to every caller.
16
- * Across PIDs the guard is treated as absent and init runs fresh —
17
- * `globalThis` is per-process, but the explicit PID check adds defensive
18
- * belt-and-suspenders against any state-leakage edge case.
19
- *
20
- * The singleton returns the same hooks reference to every caller within a
21
- * process — first and duplicate alike. OpenCode may register the same hook
22
- * surface once per configured plugin source; that is preferable to suppressing
23
- * duplicates with an empty object because every source keeps the full tools,
24
- * commands, skills, and hooks surface.
25
- *
26
- * **Known limitation — rejected init is sticky.** If `doInit()` rejects,
27
- * the rejected promise is stored on `globalThis` and every subsequent
28
- * invocation in the same PID returns the same rejection without retrying
29
- * init. This is intentional: re-running heavy init on every call would
30
- * defeat the guard's purpose. Recovery requires a process restart.
31
- */
32
- export interface PlugInOnceOptions<T> {
33
- /** Heavy init work that should run at most once per process. */
34
- doInit: () => Promise<T>;
35
- /** Test override; defaults to `process.pid`. */
36
- pid?: number;
37
- }
38
- /**
39
- * Result envelope for `plugInOnce(...)`.
40
- *
41
- * - `isFirst: true` — caller was the first invocation in this process.
42
- * - `isFirst: false` — `doInit` was skipped; the cached result is returned.
43
- *
44
- * In both cases `hooks` is the real resolved value of `doInit()`. Callers
45
- * return `result.hooks` unconditionally without inspecting `isFirst`.
46
- */
47
- export interface PlugInOnceResult<T> {
48
- isFirst: boolean;
49
- hooks: T;
50
- }
51
- /**
52
- * Run `doInit` at most once per process; on duplicate invocations return the
53
- * cached real hook surface so every config source sees the same surface.
54
- */
55
- export declare function plugInOnce<T>({ doInit, pid, }: PlugInOnceOptions<T>): Promise<PlugInOnceResult<T>>;
56
- /**
57
- * Test-only: clear the singleton state so the next invocation re-runs
58
- * init. Must not be called in production code paths.
59
- */
60
- export declare function _resetPluginSingleton(): void;
File without changes