devworkspace 0.0.0-stage → 0.1.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.
Files changed (190) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +21 -0
  3. package/README.md +128 -3
  4. package/dist/agents/claude/adapter.d.ts +15 -0
  5. package/dist/agents/claude/adapter.js +107 -0
  6. package/dist/agents/claude/adapter.js.map +1 -0
  7. package/dist/agents/claude/content.d.ts +7 -0
  8. package/dist/agents/claude/content.js +136 -0
  9. package/dist/agents/claude/content.js.map +1 -0
  10. package/dist/agents/types.d.ts +26 -0
  11. package/dist/agents/types.js +2 -0
  12. package/dist/agents/types.js.map +1 -0
  13. package/dist/cli/args.d.ts +197 -0
  14. package/dist/cli/args.js +94 -0
  15. package/dist/cli/args.js.map +1 -0
  16. package/dist/cli/help.d.ts +10 -0
  17. package/dist/cli/help.js +181 -0
  18. package/dist/cli/help.js.map +1 -0
  19. package/dist/cli/main.d.ts +2 -0
  20. package/dist/cli/main.js +19 -0
  21. package/dist/cli/main.js.map +1 -0
  22. package/dist/cli/render.d.ts +41 -0
  23. package/dist/cli/render.js +220 -0
  24. package/dist/cli/render.js.map +1 -0
  25. package/dist/cli/run.d.ts +10 -0
  26. package/dist/cli/run.js +339 -0
  27. package/dist/cli/run.js.map +1 -0
  28. package/dist/config/schemas.d.ts +76 -0
  29. package/dist/config/schemas.js +83 -0
  30. package/dist/config/schemas.js.map +1 -0
  31. package/dist/config/validate.d.ts +22 -0
  32. package/dist/config/validate.js +76 -0
  33. package/dist/config/validate.js.map +1 -0
  34. package/dist/core/migrations.d.ts +15 -0
  35. package/dist/core/migrations.js +34 -0
  36. package/dist/core/migrations.js.map +1 -0
  37. package/dist/core/version.d.ts +2 -0
  38. package/dist/core/version.js +12 -0
  39. package/dist/core/version.js.map +1 -0
  40. package/dist/core/versioned.d.ts +13 -0
  41. package/dist/core/versioned.js +16 -0
  42. package/dist/core/versioned.js.map +1 -0
  43. package/dist/core/workspace.d.ts +34 -0
  44. package/dist/core/workspace.js +92 -0
  45. package/dist/core/workspace.js.map +1 -0
  46. package/dist/decisions/decision.d.ts +31 -0
  47. package/dist/decisions/decision.js +123 -0
  48. package/dist/decisions/decision.js.map +1 -0
  49. package/dist/discovery/commands.d.ts +12 -0
  50. package/dist/discovery/commands.js +92 -0
  51. package/dist/discovery/commands.js.map +1 -0
  52. package/dist/discovery/detect.d.ts +72 -0
  53. package/dist/discovery/detect.js +421 -0
  54. package/dist/discovery/detect.js.map +1 -0
  55. package/dist/discovery/ignore.d.ts +6 -0
  56. package/dist/discovery/ignore.js +76 -0
  57. package/dist/discovery/ignore.js.map +1 -0
  58. package/dist/discovery/scan.d.ts +24 -0
  59. package/dist/discovery/scan.js +132 -0
  60. package/dist/discovery/scan.js.map +1 -0
  61. package/dist/errors/index.d.ts +25 -0
  62. package/dist/errors/index.js +39 -0
  63. package/dist/errors/index.js.map +1 -0
  64. package/dist/git/git.d.ts +27 -0
  65. package/dist/git/git.js +97 -0
  66. package/dist/git/git.js.map +1 -0
  67. package/dist/index.d.ts +26 -0
  68. package/dist/index.js +21 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/integrations/agent-skills.d.ts +10 -0
  71. package/dist/integrations/agent-skills.js +45 -0
  72. package/dist/integrations/agent-skills.js.map +1 -0
  73. package/dist/integrations/graphify.d.ts +7 -0
  74. package/dist/integrations/graphify.js +54 -0
  75. package/dist/integrations/graphify.js.map +1 -0
  76. package/dist/integrations/index.d.ts +6 -0
  77. package/dist/integrations/index.js +30 -0
  78. package/dist/integrations/index.js.map +1 -0
  79. package/dist/integrations/omniroute.d.ts +7 -0
  80. package/dist/integrations/omniroute.js +24 -0
  81. package/dist/integrations/omniroute.js.map +1 -0
  82. package/dist/integrations/ponytail.d.ts +10 -0
  83. package/dist/integrations/ponytail.js +58 -0
  84. package/dist/integrations/ponytail.js.map +1 -0
  85. package/dist/integrations/probe.d.ts +6 -0
  86. package/dist/integrations/probe.js +41 -0
  87. package/dist/integrations/probe.js.map +1 -0
  88. package/dist/integrations/types.d.ts +25 -0
  89. package/dist/integrations/types.js +2 -0
  90. package/dist/integrations/types.js.map +1 -0
  91. package/dist/knowledge/facts.d.ts +43 -0
  92. package/dist/knowledge/facts.js +195 -0
  93. package/dist/knowledge/facts.js.map +1 -0
  94. package/dist/portability/bundle.d.ts +22 -0
  95. package/dist/portability/bundle.js +102 -0
  96. package/dist/portability/bundle.js.map +1 -0
  97. package/dist/prompts/onboarding.d.ts +19 -0
  98. package/dist/prompts/onboarding.js +53 -0
  99. package/dist/prompts/onboarding.js.map +1 -0
  100. package/dist/prompts/refresh.d.ts +15 -0
  101. package/dist/prompts/refresh.js +33 -0
  102. package/dist/prompts/refresh.js.map +1 -0
  103. package/dist/prompts/requirements.d.ts +9 -0
  104. package/dist/prompts/requirements.js +34 -0
  105. package/dist/prompts/requirements.js.map +1 -0
  106. package/dist/security/files.d.ts +5 -0
  107. package/dist/security/files.js +44 -0
  108. package/dist/security/files.js.map +1 -0
  109. package/dist/security/redact.d.ts +13 -0
  110. package/dist/security/redact.js +43 -0
  111. package/dist/security/redact.js.map +1 -0
  112. package/dist/services/agent.d.ts +11 -0
  113. package/dist/services/agent.js +42 -0
  114. package/dist/services/agent.js.map +1 -0
  115. package/dist/services/analysis.d.ts +45 -0
  116. package/dist/services/analysis.js +151 -0
  117. package/dist/services/analysis.js.map +1 -0
  118. package/dist/services/common.d.ts +18 -0
  119. package/dist/services/common.js +18 -0
  120. package/dist/services/common.js.map +1 -0
  121. package/dist/services/doctor.d.ts +27 -0
  122. package/dist/services/doctor.js +223 -0
  123. package/dist/services/doctor.js.map +1 -0
  124. package/dist/services/init.d.ts +18 -0
  125. package/dist/services/init.js +135 -0
  126. package/dist/services/init.js.map +1 -0
  127. package/dist/services/knowledge.d.ts +40 -0
  128. package/dist/services/knowledge.js +72 -0
  129. package/dist/services/knowledge.js.map +1 -0
  130. package/dist/services/migrate.d.ts +16 -0
  131. package/dist/services/migrate.js +54 -0
  132. package/dist/services/migrate.js.map +1 -0
  133. package/dist/services/portability.d.ts +27 -0
  134. package/dist/services/portability.js +155 -0
  135. package/dist/services/portability.js.map +1 -0
  136. package/dist/services/records.d.ts +8 -0
  137. package/dist/services/records.js +26 -0
  138. package/dist/services/records.js.map +1 -0
  139. package/dist/services/refresh.d.ts +22 -0
  140. package/dist/services/refresh.js +64 -0
  141. package/dist/services/refresh.js.map +1 -0
  142. package/dist/services/reset.d.ts +23 -0
  143. package/dist/services/reset.js +96 -0
  144. package/dist/services/reset.js.map +1 -0
  145. package/dist/services/status.d.ts +57 -0
  146. package/dist/services/status.js +111 -0
  147. package/dist/services/status.js.map +1 -0
  148. package/dist/services/tasks.d.ts +44 -0
  149. package/dist/services/tasks.js +136 -0
  150. package/dist/services/tasks.js.map +1 -0
  151. package/dist/sessions/session.d.ts +19 -0
  152. package/dist/sessions/session.js +53 -0
  153. package/dist/sessions/session.js.map +1 -0
  154. package/dist/tasks/routing.d.ts +27 -0
  155. package/dist/tasks/routing.js +206 -0
  156. package/dist/tasks/routing.js.map +1 -0
  157. package/dist/tasks/task.d.ts +90 -0
  158. package/dist/tasks/task.js +275 -0
  159. package/dist/tasks/task.js.map +1 -0
  160. package/dist/utils/cleanup.d.ts +3 -0
  161. package/dist/utils/cleanup.js +18 -0
  162. package/dist/utils/cleanup.js.map +1 -0
  163. package/dist/utils/fs.d.ts +17 -0
  164. package/dist/utils/fs.js +164 -0
  165. package/dist/utils/fs.js.map +1 -0
  166. package/dist/utils/lock.d.ts +6 -0
  167. package/dist/utils/lock.js +77 -0
  168. package/dist/utils/lock.js.map +1 -0
  169. package/dist/utils/logger.d.ts +11 -0
  170. package/dist/utils/logger.js +18 -0
  171. package/dist/utils/logger.js.map +1 -0
  172. package/dist/utils/paths.d.ts +18 -0
  173. package/dist/utils/paths.js +95 -0
  174. package/dist/utils/paths.js.map +1 -0
  175. package/dist/utils/proc.d.ts +22 -0
  176. package/dist/utils/proc.js +74 -0
  177. package/dist/utils/proc.js.map +1 -0
  178. package/docs/architecture.md +53 -0
  179. package/docs/benchmarking.md +33 -0
  180. package/docs/claude.md +46 -0
  181. package/docs/contributing.md +64 -0
  182. package/docs/getting-started.md +79 -0
  183. package/docs/integrations.md +51 -0
  184. package/docs/knowledge.md +66 -0
  185. package/docs/portability.md +40 -0
  186. package/docs/routing.md +51 -0
  187. package/docs/security.md +44 -0
  188. package/docs/testing.md +58 -0
  189. package/docs/troubleshooting.md +27 -0
  190. package/package.json +68 -4
@@ -0,0 +1,132 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { IgnoreMatcher } from './ignore.js';
4
+ import { isSensitiveFile } from '../security/files.js';
5
+ /** Directories that are generated, vendored, or private and are never traversed. */
6
+ export const ALWAYS_SKIP_DIRS = new Set([
7
+ '.git',
8
+ '.hg',
9
+ '.svn',
10
+ 'node_modules',
11
+ '.next',
12
+ '.nuxt',
13
+ '.svelte-kit',
14
+ '.turbo',
15
+ '.parcel-cache',
16
+ 'dist',
17
+ 'build',
18
+ 'out',
19
+ 'coverage',
20
+ '.cache',
21
+ 'venv',
22
+ '.venv',
23
+ '__pycache__',
24
+ '.tox',
25
+ '.mypy_cache',
26
+ '.pytest_cache',
27
+ '.ruff_cache',
28
+ '.gradle',
29
+ '.idea',
30
+ 'target',
31
+ 'vendor',
32
+ 'obj',
33
+ 'graphify-out',
34
+ '.devworkspace',
35
+ ]);
36
+ export const DEFAULT_MAX_FILES = 50_000;
37
+ export const DEFAULT_MAX_DEPTH = 14;
38
+ export async function scanProject(root, opts = {}) {
39
+ const maxFiles = opts.maxFiles ?? DEFAULT_MAX_FILES;
40
+ const maxDepth = opts.maxDepth ?? DEFAULT_MAX_DEPTH;
41
+ const matcher = new IgnoreMatcher();
42
+ const files = new Set();
43
+ const byExtension = {};
44
+ const topCounts = new Map();
45
+ const sensitive = [];
46
+ let dirCount = 0;
47
+ let truncated = false;
48
+ let skippedSymlinks = 0;
49
+ let ignoredEntries = 0;
50
+ async function loadIgnoreFile(dirAbs, base, name) {
51
+ try {
52
+ matcher.add(await fs.readFile(path.join(dirAbs, name), 'utf8'), base);
53
+ }
54
+ catch {
55
+ /* no ignore file here */
56
+ }
57
+ }
58
+ async function walk(dirAbs, rel, depth) {
59
+ if (truncated)
60
+ return;
61
+ dirCount++;
62
+ await loadIgnoreFile(dirAbs, rel, '.gitignore');
63
+ if (depth === 0)
64
+ await loadIgnoreFile(dirAbs, '', '.devworkspaceignore');
65
+ let entries;
66
+ try {
67
+ entries = await fs.readdir(dirAbs, { withFileTypes: true });
68
+ }
69
+ catch {
70
+ return;
71
+ }
72
+ entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
73
+ for (const e of entries) {
74
+ if (truncated)
75
+ return;
76
+ const childRel = rel ? `${rel}/${e.name}` : e.name;
77
+ if (e.isSymbolicLink()) {
78
+ skippedSymlinks++;
79
+ continue;
80
+ }
81
+ if (e.isDirectory()) {
82
+ if (ALWAYS_SKIP_DIRS.has(e.name) || matcher.ignores(childRel, true)) {
83
+ ignoredEntries++;
84
+ continue;
85
+ }
86
+ if (depth + 1 > maxDepth) {
87
+ truncated = true;
88
+ continue;
89
+ }
90
+ await walk(path.join(dirAbs, e.name), childRel, depth + 1);
91
+ }
92
+ else if (e.isFile()) {
93
+ const sens = isSensitiveFile(childRel);
94
+ // Sensitive files are recorded by name only, even when gitignored: knowing one exists is useful and harmless.
95
+ if (sens)
96
+ sensitive.push(childRel);
97
+ else if (matcher.ignores(childRel, false)) {
98
+ ignoredEntries++;
99
+ continue;
100
+ }
101
+ if (files.size >= maxFiles) {
102
+ truncated = true;
103
+ return;
104
+ }
105
+ files.add(childRel);
106
+ if (!sens) {
107
+ const ext = path.posix.extname(e.name).toLowerCase() || '(none)';
108
+ byExtension[ext] = (byExtension[ext] ?? 0) + 1;
109
+ }
110
+ const top = childRel.includes('/') ? childRel.slice(0, childRel.indexOf('/')) : '.';
111
+ topCounts.set(top, (topCounts.get(top) ?? 0) + 1);
112
+ }
113
+ }
114
+ }
115
+ await walk(root, '', 0);
116
+ const topDirs = [...topCounts.entries()]
117
+ .map(([name, count]) => ({ name, files: count }))
118
+ .sort((a, b) => b.files - a.files || a.name.localeCompare(b.name))
119
+ .slice(0, 25);
120
+ return {
121
+ files,
122
+ fileCount: files.size,
123
+ dirCount,
124
+ truncated,
125
+ skippedSymlinks,
126
+ ignoredEntries,
127
+ byExtension,
128
+ topDirs,
129
+ sensitiveFiles: sensitive.sort(),
130
+ };
131
+ }
132
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/discovery/scan.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { IgnoreMatcher } from './ignore.js';\nimport { isSensitiveFile } from '../security/files.js';\n\n/** Directories that are generated, vendored, or private and are never traversed. */\nexport const ALWAYS_SKIP_DIRS = new Set([\n '.git',\n '.hg',\n '.svn',\n 'node_modules',\n '.next',\n '.nuxt',\n '.svelte-kit',\n '.turbo',\n '.parcel-cache',\n 'dist',\n 'build',\n 'out',\n 'coverage',\n '.cache',\n 'venv',\n '.venv',\n '__pycache__',\n '.tox',\n '.mypy_cache',\n '.pytest_cache',\n '.ruff_cache',\n '.gradle',\n '.idea',\n 'target',\n 'vendor',\n 'obj',\n 'graphify-out',\n '.devworkspace',\n]);\n\nexport interface ScanOptions {\n maxFiles?: number;\n maxDepth?: number;\n}\n\nexport interface ScanResult {\n /** Project-relative POSIX paths of included files (kept in memory only; never persisted wholesale). */\n files: Set<string>;\n fileCount: number;\n dirCount: number;\n truncated: boolean;\n skippedSymlinks: number;\n ignoredEntries: number;\n byExtension: Record<string, number>;\n topDirs: { name: string; files: number }[];\n sensitiveFiles: string[];\n}\n\nexport const DEFAULT_MAX_FILES = 50_000;\nexport const DEFAULT_MAX_DEPTH = 14;\n\nexport async function scanProject(root: string, opts: ScanOptions = {}): Promise<ScanResult> {\n const maxFiles = opts.maxFiles ?? DEFAULT_MAX_FILES;\n const maxDepth = opts.maxDepth ?? DEFAULT_MAX_DEPTH;\n const matcher = new IgnoreMatcher();\n const files = new Set<string>();\n const byExtension: Record<string, number> = {};\n const topCounts = new Map<string, number>();\n const sensitive: string[] = [];\n let dirCount = 0;\n let truncated = false;\n let skippedSymlinks = 0;\n let ignoredEntries = 0;\n\n async function loadIgnoreFile(dirAbs: string, base: string, name: string): Promise<void> {\n try {\n matcher.add(await fs.readFile(path.join(dirAbs, name), 'utf8'), base);\n } catch {\n /* no ignore file here */\n }\n }\n\n async function walk(dirAbs: string, rel: string, depth: number): Promise<void> {\n if (truncated) return;\n dirCount++;\n await loadIgnoreFile(dirAbs, rel, '.gitignore');\n if (depth === 0) await loadIgnoreFile(dirAbs, '', '.devworkspaceignore');\n let entries;\n try {\n entries = await fs.readdir(dirAbs, { withFileTypes: true });\n } catch {\n return;\n }\n entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));\n for (const e of entries) {\n if (truncated) return;\n const childRel = rel ? `${rel}/${e.name}` : e.name;\n if (e.isSymbolicLink()) {\n skippedSymlinks++;\n continue;\n }\n if (e.isDirectory()) {\n if (ALWAYS_SKIP_DIRS.has(e.name) || matcher.ignores(childRel, true)) {\n ignoredEntries++;\n continue;\n }\n if (depth + 1 > maxDepth) {\n truncated = true;\n continue;\n }\n await walk(path.join(dirAbs, e.name), childRel, depth + 1);\n } else if (e.isFile()) {\n const sens = isSensitiveFile(childRel);\n // Sensitive files are recorded by name only, even when gitignored: knowing one exists is useful and harmless.\n if (sens) sensitive.push(childRel);\n else if (matcher.ignores(childRel, false)) {\n ignoredEntries++;\n continue;\n }\n if (files.size >= maxFiles) {\n truncated = true;\n return;\n }\n files.add(childRel);\n if (!sens) {\n const ext = path.posix.extname(e.name).toLowerCase() || '(none)';\n byExtension[ext] = (byExtension[ext] ?? 0) + 1;\n }\n const top = childRel.includes('/') ? childRel.slice(0, childRel.indexOf('/')) : '.';\n topCounts.set(top, (topCounts.get(top) ?? 0) + 1);\n }\n }\n }\n\n await walk(root, '', 0);\n\n const topDirs = [...topCounts.entries()]\n .map(([name, count]) => ({ name, files: count }))\n .sort((a, b) => b.files - a.files || a.name.localeCompare(b.name))\n .slice(0, 25);\n\n return {\n files,\n fileCount: files.size,\n dirCount,\n truncated,\n skippedSymlinks,\n ignoredEntries,\n byExtension,\n topDirs,\n sensitiveFiles: sensitive.sort(),\n };\n}\n"]}
@@ -0,0 +1,25 @@
1
+ export type ErrorCode = 'USAGE' | 'NOT_INITIALIZED' | 'INVALID_STATE' | 'UNSAFE_PATH' | 'SCHEMA' | 'SCHEMA_TOO_NEW' | 'LOCKED' | 'IO' | 'SECURITY' | 'CONFLICT' | 'NOT_FOUND' | 'GATE' | 'INTERNAL';
2
+ export declare const EXIT: {
3
+ readonly OK: 0;
4
+ readonly FAILURE: 1;
5
+ readonly USAGE: 2;
6
+ readonly WARNINGS: 3;
7
+ readonly SIGINT: 130;
8
+ readonly SIGTERM: 143;
9
+ };
10
+ export interface ErrorOptions {
11
+ /** How the user can recover. */
12
+ hint?: string;
13
+ /** Whether workspace state may have been modified before the failure. */
14
+ stateChanged?: boolean;
15
+ cause?: unknown;
16
+ }
17
+ export declare class DevWorkspaceError extends Error {
18
+ readonly code: ErrorCode;
19
+ readonly hint: string | undefined;
20
+ readonly stateChanged: boolean;
21
+ constructor(code: ErrorCode, message: string, opts?: ErrorOptions);
22
+ get exitCode(): number;
23
+ }
24
+ export declare function isDevWorkspaceError(e: unknown): e is DevWorkspaceError;
25
+ export declare function formatError(e: unknown, debug: boolean): string;
@@ -0,0 +1,39 @@
1
+ export const EXIT = { OK: 0, FAILURE: 1, USAGE: 2, WARNINGS: 3, SIGINT: 130, SIGTERM: 143 };
2
+ export class DevWorkspaceError extends Error {
3
+ code;
4
+ hint;
5
+ stateChanged;
6
+ constructor(code, message, opts = {}) {
7
+ super(message, opts.cause === undefined ? undefined : { cause: opts.cause });
8
+ this.name = 'DevWorkspaceError';
9
+ this.code = code;
10
+ this.hint = opts.hint;
11
+ this.stateChanged = opts.stateChanged ?? false;
12
+ }
13
+ get exitCode() {
14
+ return this.code === 'USAGE' ? EXIT.USAGE : EXIT.FAILURE;
15
+ }
16
+ }
17
+ export function isDevWorkspaceError(e) {
18
+ return e instanceof DevWorkspaceError;
19
+ }
20
+ export function formatError(e, debug) {
21
+ if (isDevWorkspaceError(e)) {
22
+ const lines = [`✗ ${e.message}`];
23
+ const cause = e.cause instanceof Error ? e.cause.message : undefined;
24
+ if (cause)
25
+ lines.push(` Why: ${cause}`);
26
+ lines.push(` State: ${e.stateChanged ? 'may have been modified' : 'unchanged'}`);
27
+ if (e.hint)
28
+ lines.push(` Fix: ${e.hint}`);
29
+ if (debug && e.stack)
30
+ lines.push('', e.stack);
31
+ return lines.join('\n');
32
+ }
33
+ const msg = e instanceof Error ? e.message : String(e);
34
+ const lines = [`✗ Unexpected error: ${msg}`, ' Run again with --debug for details, or report an issue.'];
35
+ if (debug && e instanceof Error && e.stack)
36
+ lines.push('', e.stack);
37
+ return lines.join('\n');
38
+ }
39
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAeA,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAW,CAAC;AAUrG,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IACjC,IAAI,CAAY;IAChB,IAAI,CAAqB;IACzB,YAAY,CAAU;IAE/B,YAAY,IAAe,EAAE,OAAe,EAAE,OAAqB,EAAE;QACnE,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,KAAK,CAAC;IACjD,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;IAC3D,CAAC;CACF;AAED,MAAM,UAAU,mBAAmB,CAAC,CAAU;IAC5C,OAAO,CAAC,YAAY,iBAAiB,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,CAAU,EAAE,KAAc;IACpD,IAAI,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACjC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QACrE,IAAI,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,aAAa,KAAK,EAAE,CAAC,CAAC;QAC5C,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QACnF,IAAI,CAAC,CAAC,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9C,IAAI,KAAK,IAAI,CAAC,CAAC,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;QAC9C,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IACD,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,CAAC,uBAAuB,GAAG,EAAE,EAAE,2DAA2D,CAAC,CAAC;IAC1G,IAAI,KAAK,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;IACpE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC","sourcesContent":["export type ErrorCode =\n | 'USAGE'\n | 'NOT_INITIALIZED'\n | 'INVALID_STATE'\n | 'UNSAFE_PATH'\n | 'SCHEMA'\n | 'SCHEMA_TOO_NEW'\n | 'LOCKED'\n | 'IO'\n | 'SECURITY'\n | 'CONFLICT'\n | 'NOT_FOUND'\n | 'GATE'\n | 'INTERNAL';\n\nexport const EXIT = { OK: 0, FAILURE: 1, USAGE: 2, WARNINGS: 3, SIGINT: 130, SIGTERM: 143 } as const;\n\nexport interface ErrorOptions {\n /** How the user can recover. */\n hint?: string;\n /** Whether workspace state may have been modified before the failure. */\n stateChanged?: boolean;\n cause?: unknown;\n}\n\nexport class DevWorkspaceError extends Error {\n readonly code: ErrorCode;\n readonly hint: string | undefined;\n readonly stateChanged: boolean;\n\n constructor(code: ErrorCode, message: string, opts: ErrorOptions = {}) {\n super(message, opts.cause === undefined ? undefined : { cause: opts.cause });\n this.name = 'DevWorkspaceError';\n this.code = code;\n this.hint = opts.hint;\n this.stateChanged = opts.stateChanged ?? false;\n }\n\n get exitCode(): number {\n return this.code === 'USAGE' ? EXIT.USAGE : EXIT.FAILURE;\n }\n}\n\nexport function isDevWorkspaceError(e: unknown): e is DevWorkspaceError {\n return e instanceof DevWorkspaceError;\n}\n\nexport function formatError(e: unknown, debug: boolean): string {\n if (isDevWorkspaceError(e)) {\n const lines = [`✗ ${e.message}`];\n const cause = e.cause instanceof Error ? e.cause.message : undefined;\n if (cause) lines.push(` Why: ${cause}`);\n lines.push(` State: ${e.stateChanged ? 'may have been modified' : 'unchanged'}`);\n if (e.hint) lines.push(` Fix: ${e.hint}`);\n if (debug && e.stack) lines.push('', e.stack);\n return lines.join('\\n');\n }\n const msg = e instanceof Error ? e.message : String(e);\n const lines = [`✗ Unexpected error: ${msg}`, ' Run again with --debug for details, or report an issue.'];\n if (debug && e instanceof Error && e.stack) lines.push('', e.stack);\n return lines.join('\\n');\n}\n"]}
@@ -0,0 +1,27 @@
1
+ export interface GitStatusEntry {
2
+ xy: string;
3
+ path: string;
4
+ origPath?: string;
5
+ }
6
+ export interface GitState {
7
+ available: boolean;
8
+ isRepo: boolean;
9
+ branch: string | null;
10
+ head: string | null;
11
+ dirty: boolean;
12
+ staged: number;
13
+ modified: number;
14
+ untracked: number;
15
+ conflicted: number;
16
+ entries: GitStatusEntry[];
17
+ error?: string;
18
+ }
19
+ /** Parse `git status --porcelain=v1 -z` output. Pure, so it can be tested without Git. */
20
+ export declare function parsePorcelainZ(out: string): GitStatusEntry[];
21
+ export declare function summarizeEntries(entries: GitStatusEntry[]): Pick<GitState, 'staged' | 'modified' | 'untracked' | 'conflicted'>;
22
+ export declare function parseBranchName(out: string): string | null;
23
+ export declare function gitAvailable(): Promise<boolean>;
24
+ export declare function getGitState(cwd: string): Promise<GitState>;
25
+ /** Files tracked by Git (used for security checks such as "is .env committed?"). */
26
+ export declare function gitTrackedFiles(cwd: string): Promise<string[] | null>;
27
+ export declare function gitTopLevel(cwd: string): Promise<string | null>;
@@ -0,0 +1,97 @@
1
+ import { run, which } from '../utils/proc.js';
2
+ /** Parse `git status --porcelain=v1 -z` output. Pure, so it can be tested without Git. */
3
+ export function parsePorcelainZ(out) {
4
+ const parts = out.split('\0');
5
+ const entries = [];
6
+ for (let i = 0; i < parts.length; i++) {
7
+ const p = parts[i];
8
+ if (p.length < 4)
9
+ continue;
10
+ const xy = p.slice(0, 2);
11
+ const file = p.slice(3);
12
+ if (xy[0] === 'R' || xy[0] === 'C' || xy[1] === 'R' || xy[1] === 'C') {
13
+ entries.push({ xy, path: file, origPath: parts[i + 1] });
14
+ i++;
15
+ }
16
+ else {
17
+ entries.push({ xy, path: file });
18
+ }
19
+ }
20
+ return entries;
21
+ }
22
+ export function summarizeEntries(entries) {
23
+ let staged = 0;
24
+ let modified = 0;
25
+ let untracked = 0;
26
+ let conflicted = 0;
27
+ for (const { xy } of entries) {
28
+ if (xy === '??')
29
+ untracked++;
30
+ else if (xy === '!!')
31
+ continue;
32
+ else if (/^(DD|AU|UD|UA|DU|AA|UU)$/.test(xy))
33
+ conflicted++;
34
+ else {
35
+ if (xy[0] !== ' ')
36
+ staged++;
37
+ if (xy[1] !== ' ')
38
+ modified++;
39
+ }
40
+ }
41
+ return { staged, modified, untracked, conflicted };
42
+ }
43
+ export function parseBranchName(out) {
44
+ const b = out.trim();
45
+ return b && b !== 'HEAD' ? b : null;
46
+ }
47
+ const EMPTY = {
48
+ available: false,
49
+ isRepo: false,
50
+ branch: null,
51
+ head: null,
52
+ dirty: false,
53
+ staged: 0,
54
+ modified: 0,
55
+ untracked: 0,
56
+ conflicted: 0,
57
+ entries: [],
58
+ };
59
+ export async function gitAvailable() {
60
+ return (await which('git')) !== null;
61
+ }
62
+ export async function getGitState(cwd) {
63
+ if (!(await gitAvailable()))
64
+ return { ...EMPTY };
65
+ const inside = await run('git', ['rev-parse', '--is-inside-work-tree'], { cwd });
66
+ if (!inside.ok || inside.stdout.trim() !== 'true')
67
+ return { ...EMPTY, available: true };
68
+ const [branch, head, status] = await Promise.all([
69
+ run('git', ['branch', '--show-current'], { cwd }),
70
+ run('git', ['rev-parse', 'HEAD'], { cwd }),
71
+ run('git', ['status', '--porcelain=v1', '-z', '--untracked-files=normal'], { cwd, timeoutMs: 30_000 }),
72
+ ]);
73
+ const entries = status.ok ? parsePorcelainZ(status.stdout) : [];
74
+ const counts = summarizeEntries(entries);
75
+ return {
76
+ available: true,
77
+ isRepo: true,
78
+ branch: branch.ok ? parseBranchName(branch.stdout) : null,
79
+ head: head.ok ? head.stdout.trim() : null,
80
+ dirty: entries.length > 0,
81
+ entries,
82
+ ...counts,
83
+ ...(status.ok ? {} : { error: 'git status failed' }),
84
+ };
85
+ }
86
+ /** Files tracked by Git (used for security checks such as "is .env committed?"). */
87
+ export async function gitTrackedFiles(cwd) {
88
+ const r = await run('git', ['ls-files', '-z'], { cwd, timeoutMs: 30_000 });
89
+ return r.ok ? r.stdout.split('\0').filter(Boolean) : null;
90
+ }
91
+ export async function gitTopLevel(cwd) {
92
+ if (!(await gitAvailable()))
93
+ return null;
94
+ const r = await run('git', ['rev-parse', '--show-toplevel'], { cwd });
95
+ return r.ok ? r.stdout.trim() : null;
96
+ }
97
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/git/git.ts"],"names":[],"mappings":"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","sourcesContent":["import { run, which } from '../utils/proc.js';\n\nexport interface GitStatusEntry {\n xy: string;\n path: string;\n origPath?: string;\n}\n\nexport interface GitState {\n available: boolean;\n isRepo: boolean;\n branch: string | null;\n head: string | null;\n dirty: boolean;\n staged: number;\n modified: number;\n untracked: number;\n conflicted: number;\n entries: GitStatusEntry[];\n error?: string;\n}\n\n/** Parse `git status --porcelain=v1 -z` output. Pure, so it can be tested without Git. */\nexport function parsePorcelainZ(out: string): GitStatusEntry[] {\n const parts = out.split('\\0');\n const entries: GitStatusEntry[] = [];\n for (let i = 0; i < parts.length; i++) {\n const p = parts[i]!;\n if (p.length < 4) continue;\n const xy = p.slice(0, 2);\n const file = p.slice(3);\n if (xy[0] === 'R' || xy[0] === 'C' || xy[1] === 'R' || xy[1] === 'C') {\n entries.push({ xy, path: file, origPath: parts[i + 1] });\n i++;\n } else {\n entries.push({ xy, path: file });\n }\n }\n return entries;\n}\n\nexport function summarizeEntries(entries: GitStatusEntry[]): Pick<GitState, 'staged' | 'modified' | 'untracked' | 'conflicted'> {\n let staged = 0;\n let modified = 0;\n let untracked = 0;\n let conflicted = 0;\n for (const { xy } of entries) {\n if (xy === '??') untracked++;\n else if (xy === '!!') continue;\n else if (/^(DD|AU|UD|UA|DU|AA|UU)$/.test(xy)) conflicted++;\n else {\n if (xy[0] !== ' ') staged++;\n if (xy[1] !== ' ') modified++;\n }\n }\n return { staged, modified, untracked, conflicted };\n}\n\nexport function parseBranchName(out: string): string | null {\n const b = out.trim();\n return b && b !== 'HEAD' ? b : null;\n}\n\nconst EMPTY: GitState = {\n available: false,\n isRepo: false,\n branch: null,\n head: null,\n dirty: false,\n staged: 0,\n modified: 0,\n untracked: 0,\n conflicted: 0,\n entries: [],\n};\n\nexport async function gitAvailable(): Promise<boolean> {\n return (await which('git')) !== null;\n}\n\nexport async function getGitState(cwd: string): Promise<GitState> {\n if (!(await gitAvailable())) return { ...EMPTY };\n const inside = await run('git', ['rev-parse', '--is-inside-work-tree'], { cwd });\n if (!inside.ok || inside.stdout.trim() !== 'true') return { ...EMPTY, available: true };\n const [branch, head, status] = await Promise.all([\n run('git', ['branch', '--show-current'], { cwd }),\n run('git', ['rev-parse', 'HEAD'], { cwd }),\n run('git', ['status', '--porcelain=v1', '-z', '--untracked-files=normal'], { cwd, timeoutMs: 30_000 }),\n ]);\n const entries = status.ok ? parsePorcelainZ(status.stdout) : [];\n const counts = summarizeEntries(entries);\n return {\n available: true,\n isRepo: true,\n branch: branch.ok ? parseBranchName(branch.stdout) : null,\n head: head.ok ? head.stdout.trim() : null,\n dirty: entries.length > 0,\n entries,\n ...counts,\n ...(status.ok ? {} : { error: 'git status failed' }),\n };\n}\n\n/** Files tracked by Git (used for security checks such as \"is .env committed?\"). */\nexport async function gitTrackedFiles(cwd: string): Promise<string[] | null> {\n const r = await run('git', ['ls-files', '-z'], { cwd, timeoutMs: 30_000 });\n return r.ok ? r.stdout.split('\\0').filter(Boolean) : null;\n}\n\nexport async function gitTopLevel(cwd: string): Promise<string | null> {\n if (!(await gitAvailable())) return null;\n const r = await run('git', ['rev-parse', '--show-toplevel'], { cwd });\n return r.ok ? r.stdout.trim() : null;\n}\n"]}
@@ -0,0 +1,26 @@
1
+ export { packageVersion } from './core/version.js';
2
+ export { DevWorkspaceError, EXIT } from './errors/index.js';
3
+ export { initWorkspace } from './services/init.js';
4
+ export { analyzeWorkspace } from './services/analysis.js';
5
+ export { getStatus } from './services/status.js';
6
+ export { runDoctor } from './services/doctor.js';
7
+ export { refreshWorkspace } from './services/refresh.js';
8
+ export { getContext, addFact, verifyFactByKey, removeFactByKey } from './services/knowledge.js';
9
+ export { createTask, listTasksService, showTask, setTaskStatus, updateTask, recordVerification, recordTests, attestTask } from './services/tasks.js';
10
+ export { recordDecision, getDecisions, recordSession, getSessions } from './services/records.js';
11
+ export { installAgent } from './services/agent.js';
12
+ export { exportWorkspace, importWorkspace } from './services/portability.js';
13
+ export { migrateWorkspace } from './services/migrate.js';
14
+ export { classifyRequest, detectConstraints, WORKFLOWS } from './tasks/routing.js';
15
+ export { evaluateGate } from './tasks/task.js';
16
+ export { redact, findSecrets, containsSecret } from './security/redact.js';
17
+ export { isSensitiveFile } from './security/files.js';
18
+ export { buildOnboardingPrompt } from './prompts/onboarding.js';
19
+ export { ONBOARDING_REQUIREMENTS, REFRESH_REQUIREMENTS, validatePrompt } from './prompts/requirements.js';
20
+ export type { ServiceOptions } from './services/common.js';
21
+ export type { StatusResult } from './services/status.js';
22
+ export type { DoctorResult, DoctorCheck } from './services/doctor.js';
23
+ export type { Task, TaskStatus, TaskType } from './tasks/task.js';
24
+ export type { Fact, ProjectConfig, CommandEntry } from './config/schemas.js';
25
+ export type { IntegrationStatus } from './integrations/types.js';
26
+ export type { AgentAdapter, PlannedChange } from './agents/types.js';
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ // Programmatic API: the same engine that powers the CLI, for a future VS Code extension, dashboards and automation.
2
+ export { packageVersion } from './core/version.js';
3
+ export { DevWorkspaceError, EXIT } from './errors/index.js';
4
+ export { initWorkspace } from './services/init.js';
5
+ export { analyzeWorkspace } from './services/analysis.js';
6
+ export { getStatus } from './services/status.js';
7
+ export { runDoctor } from './services/doctor.js';
8
+ export { refreshWorkspace } from './services/refresh.js';
9
+ export { getContext, addFact, verifyFactByKey, removeFactByKey } from './services/knowledge.js';
10
+ export { createTask, listTasksService, showTask, setTaskStatus, updateTask, recordVerification, recordTests, attestTask } from './services/tasks.js';
11
+ export { recordDecision, getDecisions, recordSession, getSessions } from './services/records.js';
12
+ export { installAgent } from './services/agent.js';
13
+ export { exportWorkspace, importWorkspace } from './services/portability.js';
14
+ export { migrateWorkspace } from './services/migrate.js';
15
+ export { classifyRequest, detectConstraints, WORKFLOWS } from './tasks/routing.js';
16
+ export { evaluateGate } from './tasks/task.js';
17
+ export { redact, findSecrets, containsSecret } from './security/redact.js';
18
+ export { isSensitiveFile } from './security/files.js';
19
+ export { buildOnboardingPrompt } from './prompts/onboarding.js';
20
+ export { ONBOARDING_REQUIREMENTS, REFRESH_REQUIREMENTS, validatePrompt } from './prompts/requirements.js';
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,oHAAoH;AACpH,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAChG,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,kBAAkB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACrJ,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACjG,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC","sourcesContent":["// Programmatic API: the same engine that powers the CLI, for a future VS Code extension, dashboards and automation.\nexport { packageVersion } from './core/version.js';\nexport { DevWorkspaceError, EXIT } from './errors/index.js';\nexport { initWorkspace } from './services/init.js';\nexport { analyzeWorkspace } from './services/analysis.js';\nexport { getStatus } from './services/status.js';\nexport { runDoctor } from './services/doctor.js';\nexport { refreshWorkspace } from './services/refresh.js';\nexport { getContext, addFact, verifyFactByKey, removeFactByKey } from './services/knowledge.js';\nexport { createTask, listTasksService, showTask, setTaskStatus, updateTask, recordVerification, recordTests, attestTask } from './services/tasks.js';\nexport { recordDecision, getDecisions, recordSession, getSessions } from './services/records.js';\nexport { installAgent } from './services/agent.js';\nexport { exportWorkspace, importWorkspace } from './services/portability.js';\nexport { migrateWorkspace } from './services/migrate.js';\nexport { classifyRequest, detectConstraints, WORKFLOWS } from './tasks/routing.js';\nexport { evaluateGate } from './tasks/task.js';\nexport { redact, findSecrets, containsSecret } from './security/redact.js';\nexport { isSensitiveFile } from './security/files.js';\nexport { buildOnboardingPrompt } from './prompts/onboarding.js';\nexport { ONBOARDING_REQUIREMENTS, REFRESH_REQUIREMENTS, validatePrompt } from './prompts/requirements.js';\nexport type { ServiceOptions } from './services/common.js';\nexport type { StatusResult } from './services/status.js';\nexport type { DoctorResult, DoctorCheck } from './services/doctor.js';\nexport type { Task, TaskStatus, TaskType } from './tasks/task.js';\nexport type { Fact, ProjectConfig, CommandEntry } from './config/schemas.js';\nexport type { IntegrationStatus } from './integrations/types.js';\nexport type { AgentAdapter, PlannedChange } from './agents/types.js';\n"]}
@@ -0,0 +1,10 @@
1
+ import type { Integration } from './types.js';
2
+ /**
3
+ * Agent Skills is distributed as a Claude Code plugin. Documented install:
4
+ * /plugin marketplace add addyosmani/agent-skills
5
+ * /plugin install agent-skills@addy-agent-skills
6
+ * Its slash commands (/spec /plan /build /test /review /code-simplify /ship) provide the HOW;
7
+ * DevWorkspace's generated skill only decides WHEN to use them.
8
+ * Plugin state is owned by Claude Code, so detection is best effort and reported as UNVERIFIED when not found.
9
+ */
10
+ export declare const agentSkills: Integration;
@@ -0,0 +1,45 @@
1
+ import { claudeConfigMentions } from './probe.js';
2
+ const DOCS = 'https://github.com/addyosmani/agent-skills';
3
+ /**
4
+ * Agent Skills is distributed as a Claude Code plugin. Documented install:
5
+ * /plugin marketplace add addyosmani/agent-skills
6
+ * /plugin install agent-skills@addy-agent-skills
7
+ * Its slash commands (/spec /plan /build /test /review /code-simplify /ship) provide the HOW;
8
+ * DevWorkspace's generated skill only decides WHEN to use them.
9
+ * Plugin state is owned by Claude Code, so detection is best effort and reported as UNVERIFIED when not found.
10
+ */
11
+ export const agentSkills = {
12
+ id: 'agent-skills',
13
+ name: 'Agent Skills',
14
+ optional: false,
15
+ usage: 'Use the Agent Skills workflows for engineering methodology: /spec (requirements), /plan (task breakdown), /build (implementation), ' +
16
+ '/test (verification), /review (review gates), /code-simplify (reduce complexity), /ship (release). Do not re-implement them from memory; invoke them.',
17
+ async detect(ctx) {
18
+ const hit = await claudeConfigMentions(ctx, 'agent-skills');
19
+ if (hit) {
20
+ return {
21
+ id: 'agent-skills',
22
+ name: 'Agent Skills',
23
+ optional: false,
24
+ state: 'READY',
25
+ details: [`referenced in Claude Code config (${hit})`],
26
+ remediation: [],
27
+ docsUrl: DOCS,
28
+ };
29
+ }
30
+ return {
31
+ id: 'agent-skills',
32
+ name: 'Agent Skills',
33
+ optional: false,
34
+ state: 'UNVERIFIED',
35
+ details: ['not found in Claude Code settings; plugin state is held by Claude Code so this check is best-effort'],
36
+ remediation: [
37
+ 'In Claude Code run: /plugin marketplace add addyosmani/agent-skills',
38
+ 'Then: /plugin install agent-skills@addy-agent-skills',
39
+ 'Verify with /plugin, or by confirming /spec and /review appear in the slash-command list.',
40
+ ],
41
+ docsUrl: DOCS,
42
+ };
43
+ },
44
+ };
45
+ //# sourceMappingURL=agent-skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-skills.js","sourceRoot":"","sources":["../../src/integrations/agent-skills.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,IAAI,GAAG,4CAA4C,CAAC;AAE1D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAAgB;IACtC,EAAE,EAAE,cAAc;IAClB,IAAI,EAAE,cAAc;IACpB,QAAQ,EAAE,KAAK;IACf,KAAK,EACH,qIAAqI;QACrI,uJAAuJ;IACzJ,KAAK,CAAC,MAAM,CAAC,GAAG;QACd,MAAM,GAAG,GAAG,MAAM,oBAAoB,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAC5D,IAAI,GAAG,EAAE,CAAC;YACR,OAAO;gBACL,EAAE,EAAE,cAAc;gBAClB,IAAI,EAAE,cAAc;gBACpB,QAAQ,EAAE,KAAK;gBACf,KAAK,EAAE,OAAO;gBACd,OAAO,EAAE,CAAC,qCAAqC,GAAG,GAAG,CAAC;gBACtD,WAAW,EAAE,EAAE;gBACf,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QACD,OAAO;YACL,EAAE,EAAE,cAAc;YAClB,IAAI,EAAE,cAAc;YACpB,QAAQ,EAAE,KAAK;YACf,KAAK,EAAE,YAAY;YACnB,OAAO,EAAE,CAAC,qGAAqG,CAAC;YAChH,WAAW,EAAE;gBACX,qEAAqE;gBACrE,sDAAsD;gBACtD,2FAA2F;aAC5F;YACD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import type { Integration, IntegrationStatus } from './types.js';\nimport { claudeConfigMentions } from './probe.js';\n\nconst DOCS = 'https://github.com/addyosmani/agent-skills';\n\n/**\n * Agent Skills is distributed as a Claude Code plugin. Documented install:\n * /plugin marketplace add addyosmani/agent-skills\n * /plugin install agent-skills@addy-agent-skills\n * Its slash commands (/spec /plan /build /test /review /code-simplify /ship) provide the HOW;\n * DevWorkspace's generated skill only decides WHEN to use them.\n * Plugin state is owned by Claude Code, so detection is best effort and reported as UNVERIFIED when not found.\n */\nexport const agentSkills: Integration = {\n id: 'agent-skills',\n name: 'Agent Skills',\n optional: false,\n usage:\n 'Use the Agent Skills workflows for engineering methodology: /spec (requirements), /plan (task breakdown), /build (implementation), ' +\n '/test (verification), /review (review gates), /code-simplify (reduce complexity), /ship (release). Do not re-implement them from memory; invoke them.',\n async detect(ctx): Promise<IntegrationStatus> {\n const hit = await claudeConfigMentions(ctx, 'agent-skills');\n if (hit) {\n return {\n id: 'agent-skills',\n name: 'Agent Skills',\n optional: false,\n state: 'READY',\n details: [`referenced in Claude Code config (${hit})`],\n remediation: [],\n docsUrl: DOCS,\n };\n }\n return {\n id: 'agent-skills',\n name: 'Agent Skills',\n optional: false,\n state: 'UNVERIFIED',\n details: ['not found in Claude Code settings; plugin state is held by Claude Code so this check is best-effort'],\n remediation: [\n 'In Claude Code run: /plugin marketplace add addyosmani/agent-skills',\n 'Then: /plugin install agent-skills@addy-agent-skills',\n 'Verify with /plugin, or by confirming /spec and /review appear in the slash-command list.',\n ],\n docsUrl: DOCS,\n };\n },\n};\n"]}
@@ -0,0 +1,7 @@
1
+ import type { Integration } from './types.js';
2
+ /**
3
+ * Graphify builds a queryable knowledge graph in graphify-out/ and registers a Claude Code skill.
4
+ * Documented install: `uv tool install graphifyy` (or pipx/pip), then `graphify install --project`.
5
+ * DevWorkspace only detects it and tells the agent when to use it; it never re-implements graph building.
6
+ */
7
+ export declare const graphify: Integration;
@@ -0,0 +1,54 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { skillDirExists } from './probe.js';
4
+ const DOCS = 'https://github.com/Graphify-Labs/graphify';
5
+ /**
6
+ * Graphify builds a queryable knowledge graph in graphify-out/ and registers a Claude Code skill.
7
+ * Documented install: `uv tool install graphifyy` (or pipx/pip), then `graphify install --project`.
8
+ * DevWorkspace only detects it and tells the agent when to use it; it never re-implements graph building.
9
+ */
10
+ export const graphify = {
11
+ id: 'graphify',
12
+ name: 'Graphify',
13
+ optional: false,
14
+ usage: 'For "where is X / what depends on Y" questions, query the Graphify graph first (graphify query "<question>", graphify path, graphify explain) ' +
15
+ 'or use the /graphify skill. The graph is a navigation aid, not a source of truth: confirm in the actual files before relying on it.',
16
+ async detect(ctx) {
17
+ const details = [];
18
+ const remediation = [];
19
+ const cli = await ctx.which('graphify');
20
+ const skill = await skillDirExists(ctx, 'graphify');
21
+ let graphBuilt = false;
22
+ try {
23
+ const st = await fs.stat(path.join(ctx.root, 'graphify-out', 'graph.json'));
24
+ graphBuilt = st.isFile();
25
+ if (graphBuilt)
26
+ details.push(`graph built (graphify-out/graph.json, updated ${st.mtime.toISOString().slice(0, 10)})`);
27
+ }
28
+ catch {
29
+ /* not built */
30
+ }
31
+ if (cli)
32
+ details.push('graphify CLI on PATH');
33
+ if (skill)
34
+ details.push(`Claude skill installed (${skill})`);
35
+ let state;
36
+ if ((cli || skill) && graphBuilt)
37
+ state = 'READY';
38
+ else if (cli || skill) {
39
+ state = 'PARTIAL';
40
+ details.push('no graph built yet');
41
+ remediation.push('In Claude Code run: /graphify . (builds graphify-out/graph.json)');
42
+ }
43
+ else if (graphBuilt) {
44
+ state = 'PARTIAL';
45
+ remediation.push('Install the CLI: uv tool install graphifyy (or pipx install graphifyy), then: graphify install --project');
46
+ }
47
+ else {
48
+ state = 'NOT_DETECTED';
49
+ remediation.push('Install: uv tool install graphifyy (or pipx install graphifyy)', 'Register for this project: graphify install --project', 'Then in Claude Code run: /graphify .');
50
+ }
51
+ return { id: 'graphify', name: 'Graphify', optional: false, state, details, remediation, docsUrl: DOCS };
52
+ },
53
+ };
54
+ //# sourceMappingURL=graphify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphify.js","sourceRoot":"","sources":["../../src/integrations/graphify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,IAAI,GAAG,2CAA2C,CAAC;AAEzD;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAgB;IACnC,EAAE,EAAE,UAAU;IACd,IAAI,EAAE,UAAU;IAChB,QAAQ,EAAE,KAAK;IACf,KAAK,EACH,gJAAgJ;QAChJ,qIAAqI;IACvI,KAAK,CAAC,MAAM,CAAC,GAAG;QACd,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAa,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QACpD,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,cAAc,EAAE,YAAY,CAAC,CAAC,CAAC;YAC5E,UAAU,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC;YACzB,IAAI,UAAU;gBAAE,OAAO,CAAC,IAAI,CAAC,iDAAiD,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;QACxH,CAAC;QAAC,MAAM,CAAC;YACP,eAAe;QACjB,CAAC;QACD,IAAI,GAAG;YAAE,OAAO,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAC9C,IAAI,KAAK;YAAE,OAAO,CAAC,IAAI,CAAC,2BAA2B,KAAK,GAAG,CAAC,CAAC;QAE7D,IAAI,KAAiC,CAAC;QACtC,IAAI,CAAC,GAAG,IAAI,KAAK,CAAC,IAAI,UAAU;YAAE,KAAK,GAAG,OAAO,CAAC;aAC7C,IAAI,GAAG,IAAI,KAAK,EAAE,CAAC;YACtB,KAAK,GAAG,SAAS,CAAC;YAClB,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;YACnC,WAAW,CAAC,IAAI,CAAC,oEAAoE,CAAC,CAAC;QACzF,CAAC;aAAM,IAAI,UAAU,EAAE,CAAC;YACtB,KAAK,GAAG,SAAS,CAAC;YAClB,WAAW,CAAC,IAAI,CAAC,4GAA4G,CAAC,CAAC;QACjI,CAAC;aAAM,CAAC;YACN,KAAK,GAAG,cAAc,CAAC;YACvB,WAAW,CAAC,IAAI,CAAC,kEAAkE,EAAE,uDAAuD,EAAE,sCAAsC,CAAC,CAAC;QACxL,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3G,CAAC;CACF,CAAC","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { Integration, IntegrationStatus } from './types.js';\nimport { skillDirExists } from './probe.js';\n\nconst DOCS = 'https://github.com/Graphify-Labs/graphify';\n\n/**\n * Graphify builds a queryable knowledge graph in graphify-out/ and registers a Claude Code skill.\n * Documented install: `uv tool install graphifyy` (or pipx/pip), then `graphify install --project`.\n * DevWorkspace only detects it and tells the agent when to use it; it never re-implements graph building.\n */\nexport const graphify: Integration = {\n id: 'graphify',\n name: 'Graphify',\n optional: false,\n usage:\n 'For \"where is X / what depends on Y\" questions, query the Graphify graph first (graphify query \"<question>\", graphify path, graphify explain) ' +\n 'or use the /graphify skill. The graph is a navigation aid, not a source of truth: confirm in the actual files before relying on it.',\n async detect(ctx): Promise<IntegrationStatus> {\n const details: string[] = [];\n const remediation: string[] = [];\n const cli = await ctx.which('graphify');\n const skill = await skillDirExists(ctx, 'graphify');\n let graphBuilt = false;\n try {\n const st = await fs.stat(path.join(ctx.root, 'graphify-out', 'graph.json'));\n graphBuilt = st.isFile();\n if (graphBuilt) details.push(`graph built (graphify-out/graph.json, updated ${st.mtime.toISOString().slice(0, 10)})`);\n } catch {\n /* not built */\n }\n if (cli) details.push('graphify CLI on PATH');\n if (skill) details.push(`Claude skill installed (${skill})`);\n\n let state: IntegrationStatus['state'];\n if ((cli || skill) && graphBuilt) state = 'READY';\n else if (cli || skill) {\n state = 'PARTIAL';\n details.push('no graph built yet');\n remediation.push('In Claude Code run: /graphify . (builds graphify-out/graph.json)');\n } else if (graphBuilt) {\n state = 'PARTIAL';\n remediation.push('Install the CLI: uv tool install graphifyy (or pipx install graphifyy), then: graphify install --project');\n } else {\n state = 'NOT_DETECTED';\n remediation.push('Install: uv tool install graphifyy (or pipx install graphifyy)', 'Register for this project: graphify install --project', 'Then in Claude Code run: /graphify .');\n }\n return { id: 'graphify', name: 'Graphify', optional: false, state, details, remediation, docsUrl: DOCS };\n },\n};\n"]}
@@ -0,0 +1,6 @@
1
+ import type { Integration, IntegrationContext, IntegrationStatus } from './types.js';
2
+ export type { Integration, IntegrationContext, IntegrationStatus, IntegrationState } from './types.js';
3
+ export declare const INTEGRATIONS: Integration[];
4
+ export declare function defaultIntegrationContext(root: string, env?: NodeJS.ProcessEnv, home?: string): IntegrationContext;
5
+ /** Detect every integration. A failing adapter degrades to an ERROR status instead of breaking the workspace. */
6
+ export declare function detectIntegrations(ctx: IntegrationContext, list?: Integration[]): Promise<IntegrationStatus[]>;
@@ -0,0 +1,30 @@
1
+ import os from 'node:os';
2
+ import { which } from '../utils/proc.js';
3
+ import { agentSkills } from './agent-skills.js';
4
+ import { graphify } from './graphify.js';
5
+ import { omniroute } from './omniroute.js';
6
+ import { ponytail } from './ponytail.js';
7
+ export const INTEGRATIONS = [graphify, agentSkills, ponytail, omniroute];
8
+ export function defaultIntegrationContext(root, env = process.env, home = os.homedir()) {
9
+ return { root, home, env, which: (n) => which(n, env) };
10
+ }
11
+ /** Detect every integration. A failing adapter degrades to an ERROR status instead of breaking the workspace. */
12
+ export async function detectIntegrations(ctx, list = INTEGRATIONS) {
13
+ return Promise.all(list.map(async (i) => {
14
+ try {
15
+ return await i.detect(ctx);
16
+ }
17
+ catch (e) {
18
+ return {
19
+ id: i.id,
20
+ name: i.name,
21
+ optional: i.optional,
22
+ state: 'ERROR',
23
+ details: [`detection failed: ${e.message}`],
24
+ remediation: ['Run "devworkspace doctor --debug" for details.'],
25
+ docsUrl: '',
26
+ };
27
+ }
28
+ }));
29
+ }
30
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/integrations/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAKzC,MAAM,CAAC,MAAM,YAAY,GAAkB,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;AAExF,MAAM,UAAU,yBAAyB,CAAC,IAAY,EAAE,MAAyB,OAAO,CAAC,GAAG,EAAE,OAAe,EAAE,CAAC,OAAO,EAAE;IACvH,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED,iHAAiH;AACjH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAuB,EAAE,OAAsB,YAAY;IAClG,OAAO,OAAO,CAAC,GAAG,CAChB,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EAA8B,EAAE;QAC/C,IAAI,CAAC;YACH,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO;gBACL,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,KAAK,EAAE,OAAO;gBACd,OAAO,EAAE,CAAC,qBAAsB,CAAW,CAAC,OAAO,EAAE,CAAC;gBACtD,WAAW,EAAE,CAAC,gDAAgD,CAAC;gBAC/D,OAAO,EAAE,EAAE;aACZ,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CACH,CAAC;AACJ,CAAC","sourcesContent":["import os from 'node:os';\nimport { which } from '../utils/proc.js';\nimport { agentSkills } from './agent-skills.js';\nimport { graphify } from './graphify.js';\nimport { omniroute } from './omniroute.js';\nimport { ponytail } from './ponytail.js';\nimport type { Integration, IntegrationContext, IntegrationStatus } from './types.js';\n\nexport type { Integration, IntegrationContext, IntegrationStatus, IntegrationState } from './types.js';\n\nexport const INTEGRATIONS: Integration[] = [graphify, agentSkills, ponytail, omniroute];\n\nexport function defaultIntegrationContext(root: string, env: NodeJS.ProcessEnv = process.env, home: string = os.homedir()): IntegrationContext {\n return { root, home, env, which: (n) => which(n, env) };\n}\n\n/** Detect every integration. A failing adapter degrades to an ERROR status instead of breaking the workspace. */\nexport async function detectIntegrations(ctx: IntegrationContext, list: Integration[] = INTEGRATIONS): Promise<IntegrationStatus[]> {\n return Promise.all(\n list.map(async (i): Promise<IntegrationStatus> => {\n try {\n return await i.detect(ctx);\n } catch (e) {\n return {\n id: i.id,\n name: i.name,\n optional: i.optional,\n state: 'ERROR',\n details: [`detection failed: ${(e as Error).message}`],\n remediation: ['Run \"devworkspace doctor --debug\" for details.'],\n docsUrl: '',\n };\n }\n }),\n );\n}\n"]}
@@ -0,0 +1,7 @@
1
+ import type { Integration } from './types.js';
2
+ /**
3
+ * OmniRoute (model routing) is OPTIONAL and outside the V1 execution path.
4
+ * This adapter only reports whether an `omniroute` executable exists so a future provider
5
+ * boundary has somewhere to plug in. Nothing in DevWorkspace requires it, API keys, or model configuration.
6
+ */
7
+ export declare const omniroute: Integration;