terse-cli 0.1.16 → 0.1.17

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 (122) hide show
  1. package/dist/assertProjectRoot.d.ts +3 -2
  2. package/dist/assertProjectRoot.d.ts.map +1 -1
  3. package/dist/assertProjectRoot.js +5 -8
  4. package/dist/assertProjectRoot.js.map +1 -1
  5. package/dist/deploy.d.ts +2 -1
  6. package/dist/deploy.d.ts.map +1 -1
  7. package/dist/deploy.js +11 -12
  8. package/dist/deploy.js.map +1 -1
  9. package/dist/generate.d.ts +2 -1
  10. package/dist/generate.d.ts.map +1 -1
  11. package/dist/generate.js +12 -11
  12. package/dist/generate.js.map +1 -1
  13. package/dist/index.js +10 -7
  14. package/dist/index.js.map +1 -1
  15. package/dist/init.d.ts +2 -1
  16. package/dist/init.d.ts.map +1 -1
  17. package/dist/init.js +29 -57
  18. package/dist/init.js.map +1 -1
  19. package/dist/loadJob.d.ts +4 -3
  20. package/dist/loadJob.d.ts.map +1 -1
  21. package/dist/loadJob.js +6 -54
  22. package/dist/loadJob.js.map +1 -1
  23. package/dist/providers/LanguageProvider.d.ts +31 -0
  24. package/dist/providers/LanguageProvider.d.ts.map +1 -0
  25. package/dist/providers/LanguageProvider.js +2 -0
  26. package/dist/providers/LanguageProvider.js.map +1 -0
  27. package/dist/providers/codegenTypes.d.ts +130 -0
  28. package/dist/providers/codegenTypes.d.ts.map +1 -0
  29. package/dist/providers/codegenTypes.js +2 -0
  30. package/dist/providers/codegenTypes.js.map +1 -0
  31. package/dist/providers/python/PythonProvider.d.ts +33 -0
  32. package/dist/providers/python/PythonProvider.d.ts.map +1 -0
  33. package/dist/providers/python/PythonProvider.js +369 -0
  34. package/dist/providers/python/PythonProvider.js.map +1 -0
  35. package/dist/providers/python/preparePythonCodegenData.d.ts +73 -0
  36. package/dist/providers/python/preparePythonCodegenData.d.ts.map +1 -0
  37. package/dist/providers/python/preparePythonCodegenData.js +338 -0
  38. package/dist/providers/python/preparePythonCodegenData.js.map +1 -0
  39. package/dist/providers/python/pythonTemplateEngine.d.ts +3 -0
  40. package/dist/providers/python/pythonTemplateEngine.d.ts.map +1 -0
  41. package/dist/providers/python/pythonTemplateEngine.js +30 -0
  42. package/dist/providers/python/pythonTemplateEngine.js.map +1 -0
  43. package/dist/providers/resolveProvider.d.ts +8 -0
  44. package/dist/providers/resolveProvider.d.ts.map +1 -0
  45. package/dist/providers/resolveProvider.js +47 -0
  46. package/dist/providers/resolveProvider.js.map +1 -0
  47. package/dist/providers/shared/sessionStream.d.ts +24 -0
  48. package/dist/providers/shared/sessionStream.d.ts.map +1 -0
  49. package/dist/providers/shared/sessionStream.js +159 -0
  50. package/dist/providers/shared/sessionStream.js.map +1 -0
  51. package/dist/providers/shared/shellUtils.d.ts +13 -0
  52. package/dist/providers/shared/shellUtils.d.ts.map +1 -0
  53. package/dist/providers/shared/shellUtils.js +70 -0
  54. package/dist/providers/shared/shellUtils.js.map +1 -0
  55. package/dist/providers/templateUtils.d.ts +7 -0
  56. package/dist/providers/templateUtils.d.ts.map +1 -0
  57. package/dist/providers/templateUtils.js +30 -0
  58. package/dist/providers/templateUtils.js.map +1 -0
  59. package/dist/providers/typescript/TypeScriptProvider.d.ts +33 -0
  60. package/dist/providers/typescript/TypeScriptProvider.d.ts.map +1 -0
  61. package/dist/providers/typescript/TypeScriptProvider.js +162 -0
  62. package/dist/providers/typescript/TypeScriptProvider.js.map +1 -0
  63. package/dist/providers/typescript/prepareCodegenData.d.ts +145 -0
  64. package/dist/providers/typescript/prepareCodegenData.d.ts.map +1 -0
  65. package/dist/providers/typescript/prepareCodegenData.js +834 -0
  66. package/dist/providers/typescript/prepareCodegenData.js.map +1 -0
  67. package/dist/providers/typescript/templateEngine.d.ts +3 -0
  68. package/dist/providers/typescript/templateEngine.d.ts.map +1 -0
  69. package/dist/providers/typescript/templateEngine.js +37 -0
  70. package/dist/providers/typescript/templateEngine.js.map +1 -0
  71. package/dist/run.d.ts +2 -11
  72. package/dist/run.d.ts.map +1 -1
  73. package/dist/run.js +5 -192
  74. package/dist/run.js.map +1 -1
  75. package/dist/shared/types.d.ts +899 -107
  76. package/dist/shared/types.d.ts.map +1 -1
  77. package/dist/test.d.ts +2 -1
  78. package/dist/test.d.ts.map +1 -1
  79. package/dist/test.js +5 -5
  80. package/dist/test.js.map +1 -1
  81. package/package.json +2 -2
  82. package/templates/python/codegen/generated.hbs +6 -0
  83. package/templates/python/codegen/partials/attio-list-objects-method.hbs +8 -0
  84. package/templates/python/codegen/partials/attio-object-types.hbs +23 -0
  85. package/templates/python/codegen/partials/attio-query-records-method.hbs +45 -0
  86. package/templates/python/codegen/partials/attio-section.hbs +111 -0
  87. package/templates/python/codegen/partials/attio-upsert-record-method.hbs +51 -0
  88. package/templates/python/codegen/partials/deterministic-tools-section.hbs +194 -0
  89. package/templates/python/codegen/partials/file-header.hbs +32 -0
  90. package/templates/python/codegen/partials/schedule-section.hbs +9 -0
  91. package/templates/python/codegen/partials/snowflake-section.hbs +26 -0
  92. package/templates/python/codegen/partials/tool-name-types.hbs +15 -0
  93. package/templates/python/init/.python-version.hbs +1 -0
  94. package/templates/python/init/README.md.hbs +38 -0
  95. package/templates/python/init/gitignore.hbs +3 -0
  96. package/templates/python/init/pyproject.toml.hbs +16 -0
  97. package/templates/python/init/src/main.py.hbs +21 -0
  98. package/templates/typescript/codegen/generated.hbs +17 -0
  99. package/templates/typescript/codegen/partials/atlassian-section.hbs +32 -0
  100. package/templates/typescript/codegen/partials/attio-section.hbs +38 -0
  101. package/templates/typescript/codegen/partials/datadog-section.hbs +11 -0
  102. package/templates/typescript/codegen/partials/figma-section.hbs +13 -0
  103. package/templates/typescript/codegen/partials/github-section.hbs +62 -0
  104. package/templates/typescript/codegen/partials/gmail-section.hbs +21 -0
  105. package/templates/typescript/codegen/partials/header.hbs +12 -0
  106. package/templates/typescript/codegen/partials/launchdarkly-section.hbs +11 -0
  107. package/templates/typescript/codegen/partials/linear-section.hbs +27 -0
  108. package/templates/typescript/codegen/partials/notion-section.hbs +15 -0
  109. package/templates/typescript/codegen/partials/posthog-section.hbs +11 -0
  110. package/templates/typescript/codegen/partials/resource-class.hbs +9 -0
  111. package/templates/typescript/codegen/partials/slack-section.hbs +23 -0
  112. package/templates/typescript/codegen/partials/snowflake-section.hbs +9 -0
  113. package/templates/typescript/codegen/partials/system-section.hbs +18 -0
  114. package/templates/typescript/codegen/partials/tools-section.hbs +68 -0
  115. package/templates/typescript/codegen/partials/workos-section.hbs +53 -0
  116. package/templates/typescript/init/env.example.hbs +1 -0
  117. package/templates/{package.json.tmpl → typescript/init/package.json.hbs} +1 -1
  118. package/templates/{src/index.ts.tmpl → typescript/init/src/index.ts.hbs} +1 -3
  119. /package/templates/{env.example.tmpl → python/init/env.example.hbs} +0 -0
  120. /package/templates/{.claude/settings.json.tmpl → typescript/init/.claude/settings.json.hbs} +0 -0
  121. /package/templates/{gitignore.tmpl → typescript/init/gitignore.hbs} +0 -0
  122. /package/templates/{tsconfig.json.tmpl → typescript/init/tsconfig.json.hbs} +0 -0
@@ -0,0 +1,194 @@
1
+
2
+
3
+ # === Deterministic tools ===
4
+
5
+ {{#if hasAttioTools}}
6
+ _ATTIO_FILTER_ATTRS_BY_OBJECT: dict[str, frozenset[str]] = {
7
+ {{#each attio.objects}}
8
+ {{#if hasAttributes}}
9
+ {{{apiSlugRepr}}}: {{{attrSlugsFrozensetRepr}}},
10
+ {{/if}}
11
+ {{/each}}
12
+ }
13
+
14
+
15
+ def _attio_invalid_filter_error(obj_slug: str, attribute: str | None = None) -> ValueError:
16
+ message = f"Invalid Attio filter for object {obj_slug!r}"
17
+ if attribute is not None:
18
+ message += f" on attribute {attribute!r}"
19
+ message += ": list shorthand is not supported."
20
+ if attribute == 'domains':
21
+ message += ' Use scalar shorthand "useterse.ai" or explicit Attio syntax {"domain": {"$eq": "useterse.ai"}}.'
22
+ else:
23
+ message += " Use a scalar shorthand or explicit Attio operator/object syntax instead."
24
+ return ValueError(message)
25
+
26
+
27
+ def _validate_attio_filter_node(
28
+ obj_slug: str,
29
+ value: object,
30
+ *,
31
+ attr_slugs: frozenset[str],
32
+ current_attribute: str | None = None,
33
+ ) -> None:
34
+ if isinstance(value, dict):
35
+ for raw_key, child in value.items():
36
+ if not isinstance(raw_key, str):
37
+ raise ValueError(f"Invalid Attio filter for object {obj_slug!r}: filter keys must be strings.")
38
+ key = raw_key
39
+ if key in ('$and', '$or'):
40
+ if not isinstance(child, list):
41
+ raise ValueError(f"Invalid Attio filter for object {obj_slug!r}: {key} must be a list.")
42
+ for item in child:
43
+ if not isinstance(item, dict):
44
+ raise ValueError(f"Invalid Attio filter for object {obj_slug!r}: {key} entries must be objects.")
45
+ _validate_attio_filter_node(obj_slug, item, attr_slugs=attr_slugs)
46
+ continue
47
+ if key == '$not':
48
+ if not isinstance(child, dict):
49
+ raise ValueError(f"Invalid Attio filter for object {obj_slug!r}: $not must be an object.")
50
+ _validate_attio_filter_node(obj_slug, child, attr_slugs=attr_slugs)
51
+ continue
52
+ if key == '$in':
53
+ if not isinstance(child, list):
54
+ raise ValueError(f"Invalid Attio filter for object {obj_slug!r}: $in must be a list.")
55
+ continue
56
+ if key == 'path':
57
+ if not isinstance(child, list):
58
+ raise ValueError(f"Invalid Attio filter for object {obj_slug!r}: path must be a list.")
59
+ continue
60
+ if key == 'constraints':
61
+ if not isinstance(child, dict):
62
+ raise ValueError(f"Invalid Attio filter for object {obj_slug!r}: constraints must be an object.")
63
+ _validate_attio_filter_node(
64
+ obj_slug,
65
+ child,
66
+ attr_slugs=attr_slugs,
67
+ current_attribute=current_attribute,
68
+ )
69
+ continue
70
+
71
+ attribute: str | None = key if key in attr_slugs else current_attribute
72
+ if isinstance(child, list):
73
+ raise _attio_invalid_filter_error(obj_slug, attribute)
74
+ _validate_attio_filter_node(
75
+ obj_slug,
76
+ child,
77
+ attr_slugs=attr_slugs,
78
+ current_attribute=attribute,
79
+ )
80
+
81
+
82
+ def _serialize_attio_filter(obj_slug: str, value: object | None) -> str | None:
83
+ if value is None:
84
+ return None
85
+
86
+ parsed = value
87
+ if isinstance(value, str):
88
+ try:
89
+ parsed = json.loads(value)
90
+ except json.JSONDecodeError as exc:
91
+ raise ValueError(f"Attio filters for object {obj_slug!r} must be valid JSON objects.") from exc
92
+
93
+ if not isinstance(parsed, dict):
94
+ raise ValueError(f"Attio filters for object {obj_slug!r} must be objects.")
95
+
96
+ _validate_attio_filter_node(
97
+ obj_slug,
98
+ parsed,
99
+ attr_slugs=_ATTIO_FILTER_ATTRS_BY_OBJECT.get(obj_slug, frozenset()),
100
+ )
101
+ return json.dumps(parsed, separators=(',', ':'), sort_keys=True)
102
+
103
+
104
+ def _serialize_attio_records(records: Sequence[object]) -> str:
105
+ return json.dumps(records, separators=(',', ':'), sort_keys=True)
106
+
107
+
108
+ class _AttioTools:
109
+ def __init__(self, agent: _SdkTerseAgent) -> None:
110
+ self._agent = agent
111
+ {{#each attio.tools}}
112
+ {{#if (eq name "attio_list_objects")}}{{> attio-list-objects-method tool=this attio=../attio}}{{/if}}
113
+ {{#if (eq name "attio_query_records")}}{{> attio-query-records-method tool=this attio=../attio}}{{/if}}
114
+ {{#if (eq name "attio_upsert_record")}}{{> attio-upsert-record-method tool=this attio=../attio}}{{/if}}
115
+ {{/each}}
116
+ {{/if}}
117
+ {{#if hasSnowflakeTools}}
118
+
119
+ class _SnowflakeTools:
120
+ def __init__(self, agent: _SdkTerseAgent) -> None:
121
+ self._agent = agent
122
+ {{#each snowflake.tools}}
123
+
124
+ def {{methodName}}(self, query: str) -> {{outputModel}}:
125
+ return {{outputModel}}.model_validate(
126
+ self._agent.execute_tool(
127
+ {{{nameRepr}}},
128
+ {'integrationId': {{{../snowflake.instanceIdRepr}}}, 'query': query},
129
+ )
130
+ )
131
+ {{/each}}
132
+ {{/if}}
133
+ {{#if hasAnyDeterministicTools}}
134
+
135
+ class GeneratedTools:
136
+ {{#if hasAttioTools}}
137
+ attio: _AttioTools
138
+ {{/if}}
139
+ {{#if hasSnowflakeTools}}
140
+ snowflake: _SnowflakeTools
141
+ {{/if}}
142
+
143
+ def __init__(
144
+ self,
145
+ {{#if hasAttioTools}}
146
+ attio: _AttioTools | None = None,
147
+ {{/if}}
148
+ {{#if hasSnowflakeTools}}
149
+ snowflake: _SnowflakeTools | None = None,
150
+ {{/if}}
151
+ ) -> None:
152
+ {{#if hasAttioTools}}
153
+ if attio is not None:
154
+ self.attio = attio
155
+ {{/if}}
156
+ {{#if hasSnowflakeTools}}
157
+ if snowflake is not None:
158
+ self.snowflake = snowflake
159
+ {{/if}}
160
+ {{else}}
161
+
162
+ class GeneratedTools:
163
+ pass
164
+ {{/if}}
165
+
166
+
167
+ class TerseAgent(_SdkTerseAgent):
168
+ tools: GeneratedTools
169
+
170
+
171
+ def create_tools(agent: _SdkTerseAgent) -> GeneratedTools:
172
+ allowed = {skill.integration_type for skill in agent.skills}
173
+ {{#if hasAnyDeterministicTools}}
174
+ return GeneratedTools(
175
+ {{#if hasAttioTools}}
176
+ attio=_AttioTools(agent) if 'attio' in allowed else None,
177
+ {{/if}}
178
+ {{#if hasSnowflakeTools}}
179
+ snowflake=_SnowflakeTools(agent) if 'snowflake' in allowed else None,
180
+ {{/if}}
181
+ )
182
+ {{else}}
183
+ _ = allowed
184
+ return GeneratedTools()
185
+ {{/if}}
186
+
187
+
188
+ def attach_tools(agent: _SdkTerseAgent) -> GeneratedTools:
189
+ tools = create_tools(agent)
190
+ agent.attach_tools(tools)
191
+ return tools
192
+
193
+
194
+ __all__ = {{{exportedNamesRepr}}}
@@ -0,0 +1,32 @@
1
+ """
2
+ Generated by `terse generate`. Do not edit manually.
3
+
4
+ Type checking:
5
+ uv run ty check src/
6
+ """
7
+
8
+ from __future__ import annotations
9
+ {{#if typingImportsLine}}
10
+
11
+ {{typingImportsLine}}
12
+ {{/if}}
13
+ {{#if pydanticImportsLine}}
14
+
15
+ {{pydanticImportsLine}}
16
+ {{/if}}
17
+ {{#if needsJsonImport}}
18
+
19
+ import json
20
+ {{/if}}
21
+
22
+ from terse_sdk import (
23
+ {{#each sdkImports}}
24
+ {{this}},
25
+ {{/each}}
26
+ )
27
+
28
+
29
+ def _compact_dict(**values: object) -> dict[str, object]:
30
+ """Drop None values while preserving falsey config values like False and []."""
31
+
32
+ return {key: value for key, value in values.items() if value is not None}
@@ -0,0 +1,9 @@
1
+
2
+
3
+ # === Schedule ===
4
+
5
+
6
+ class Schedule:
7
+ @staticmethod
8
+ def cron(expression: str) -> TriggerConfig:
9
+ return TriggerConfig(integration_id='system', integration_type='cron_job', config_type='time_trigger', config=_compact_dict(cronExpression=expression))
@@ -0,0 +1,26 @@
1
+ {{#if snowflake}}
2
+
3
+
4
+ # === Snowflake ===
5
+
6
+
7
+ class Snowflake:
8
+ @staticmethod
9
+ def skill() -> SkillConfig[{{snowflake.skillToolType}}]:
10
+ return SkillConfig(integration_id={{{snowflake.instanceIdRepr}}}, integration_type='snowflake', config_type='snowflake_output', config=_compact_dict())
11
+ {{else}}
12
+
13
+
14
+ # === Snowflake ===
15
+
16
+
17
+ class Snowflake:
18
+ @staticmethod
19
+ def skill() -> SkillConfig[str]:
20
+ return SkillConfig(
21
+ integration_id='snowflake_placeholder',
22
+ integration_type='snowflake',
23
+ config_type='snowflake_output',
24
+ config=_compact_dict(),
25
+ )
26
+ {{/if}}
@@ -0,0 +1,15 @@
1
+ {{#if hasAnyToolNameTypes}}
2
+
3
+
4
+ # === Tool name types ===
5
+
6
+ {{#if attioToolNamesLiteral}}
7
+ AttioToolNames = Literal[{{{attioToolNamesLiteral}}}]
8
+ {{/if}}
9
+ {{#if snowflakeToolNamesLiteral}}
10
+ SnowflakeToolNames = Literal[{{{snowflakeToolNamesLiteral}}}]
11
+ {{/if}}
12
+ {{#if allToolNamesLiteral}}
13
+ AllToolNames = Literal[{{{allToolNamesLiteral}}}]
14
+ {{/if}}
15
+ {{/if}}
@@ -0,0 +1 @@
1
+ 3.11
@@ -0,0 +1,38 @@
1
+ # {{projectName}}
2
+
3
+ Project scaffolded by `terse-cli`.
4
+
5
+ ## Prerequisites
6
+
7
+ - Python `>=3.11`
8
+ - [`uv`](https://docs.astral.sh/uv/getting-started/installation/) for dependency management and local commands
9
+
10
+ If `terse init` did not install dependencies automatically, install `uv` and run:
11
+
12
+ ```bash
13
+ uv sync
14
+ ```
15
+
16
+ ## Quick Start
17
+
18
+ ```bash
19
+ uv sync
20
+ terse test
21
+ terse deploy
22
+ ```
23
+
24
+ Edit `src/main.py` to register jobs with `@app.job(...)`.
25
+
26
+ ## Generated Helpers
27
+
28
+ The starter scaffold registers `Attio.skill()` and `Snowflake.skill()` by default.
29
+
30
+ After you connect Attio or Snowflake in Terse, rerun `terse generate` to refresh `src/terse_generated.py` with the real integration IDs and deterministic tool wrappers.
31
+
32
+ Import helpers like `Attio`, `AttioObject`, `Schedule`, `Snowflake`, and `TerseAgent` from `terse_generated`.
33
+
34
+ If a job registers `skills=[Snowflake.skill()]`, it can call deterministic wrappers like:
35
+
36
+ ```python
37
+ agent.tools.snowflake.execute_query(query="select 1")
38
+ ```
@@ -0,0 +1,3 @@
1
+ .venv
2
+ __pycache__
3
+ .env
@@ -0,0 +1,16 @@
1
+ [project]
2
+ name = "{{projectName}}"
3
+ version = "0.1.0"
4
+ requires-python = ">=3.11"
5
+ dependencies = [
6
+ "{{sdkDependency}}",
7
+ ]
8
+
9
+ [tool.uv]
10
+ package = false
11
+
12
+ [dependency-groups]
13
+ dev = ["ty"]
14
+
15
+ [tool.ty.environment]
16
+ python = ".venv"
@@ -0,0 +1,21 @@
1
+ from terse_sdk import CronJobInputEvent, SdkAgentStreamEventFinalOutput, Terse
2
+ from terse_generated import Attio, Schedule, Snowflake, TerseAgent
3
+
4
+ app = Terse()
5
+
6
+ @app.job(
7
+ name="{{projectName}}",
8
+ triggers=[Schedule.cron("0 9 * * 1")],
9
+ skills=[
10
+ Attio.skill(),
11
+ Snowflake.skill(),
12
+ ],
13
+ )
14
+ def run_job(event: CronJobInputEvent, agent: TerseAgent) -> None:
15
+ prompt = (
16
+ "Tell a joke"
17
+ f"Context: {event.formatted_content}"
18
+ )
19
+ for stream_event in agent.run(prompt, event):
20
+ if isinstance(stream_event, SdkAgentStreamEventFinalOutput):
21
+ print(stream_event.final_output)
@@ -0,0 +1,17 @@
1
+ {{> header}}
2
+
3
+ {{#if github}}{{> github-section github}}{{/if~}}
4
+ {{#if gmail}}{{> gmail-section gmail}}{{/if~}}
5
+ {{#if slack}}{{> slack-section slack}}{{/if~}}
6
+ {{#if figma}}{{> figma-section figma}}{{/if~}}
7
+ {{#if linear}}{{> linear-section linear}}{{/if~}}
8
+ {{#if atlassian}}{{> atlassian-section atlassian}}{{/if~}}
9
+ {{#if notion}}{{> notion-section notion}}{{/if~}}
10
+ {{#if posthog}}{{> posthog-section posthog}}{{/if~}}
11
+ {{#if datadog}}{{> datadog-section datadog}}{{/if~}}
12
+ {{#if launchdarkly}}{{> launchdarkly-section launchdarkly}}{{/if~}}
13
+ {{#if workos}}{{> workos-section workos}}{{/if~}}
14
+ {{#if attio}}{{> attio-section attio}}{{/if~}}
15
+ {{#if snowflake}}{{> snowflake-section snowflake}}{{/if~}}
16
+ {{#if tools}}{{> tools-section tools}}{{/if}}
17
+ {{> system-section system}}
@@ -0,0 +1,32 @@
1
+ {{sectionHeader "Jira & Confluence"}}
2
+
3
+ {{> resource-class jiraProjectClass}}
4
+
5
+ {{> resource-class confluencePageClass}}
6
+
7
+ export const Jira = {
8
+ /** Trigger when a Jira issue is created */
9
+ onIssueCreated(opts?: { project?: JiraProject }): JiraConfig {
10
+ return new JiraConfig("{{escape id}}", opts?.project?.projectKey, opts?.project?.projectId, [JiraEventType.ISSUE_CREATED])
11
+ },
12
+ /** Trigger when a Jira issue is updated */
13
+ onIssueUpdated(opts?: { project?: JiraProject }): JiraConfig {
14
+ return new JiraConfig("{{escape id}}", opts?.project?.projectKey, opts?.project?.projectId, [JiraEventType.ISSUE_UPDATED])
15
+ },
16
+ /** Trigger on all Jira events */
17
+ trigger(opts?: { project?: JiraProject; eventTypes?: JiraEventType[] }): JiraConfig {
18
+ return new JiraConfig("{{escape id}}", opts?.project?.projectKey, opts?.project?.projectId, opts?.eventTypes)
19
+ },
20
+ /** Use in `skills[]` */
21
+ skill(opts?: { project?: JiraProject }): TypedSkill<{{{skillToolType}}}> {
22
+ return new JiraConfig("{{escape id}}", opts?.project?.projectKey, opts?.project?.projectId) as TypedSkill<{{{skillToolType}}}>
23
+ },
24
+ }
25
+
26
+ export const Confluence = {
27
+ /** Use in `skills[]` */
28
+ skill(opts: { page: ConfluencePage }): TypedSkill<{{{skillToolType}}}> {
29
+ return new ConfluenceConfig("{{escape id}}", opts.page.spaceName, opts.page.spaceId, opts.page.pageId, opts.page.title) as TypedSkill<{{{skillToolType}}}>
30
+ },
31
+ }
32
+
@@ -0,0 +1,38 @@
1
+ {{sectionHeader "Attio"}}
2
+
3
+ export type AttioAttributeDefinition<TSlug extends string = string, TType extends string = string> = {
4
+ apiSlug: TSlug
5
+ title?: string
6
+ type?: TType
7
+ isRequired?: boolean
8
+ isUnique?: boolean
9
+ }
10
+
11
+ export class AttioObject<
12
+ TSlug extends string = string,
13
+ TRecordValues extends Record<string, unknown> = Record<string, unknown>,
14
+ TInputValues extends Record<string, unknown> = TRecordValues
15
+ > {
16
+ constructor(
17
+ public readonly apiSlug: TSlug,
18
+ public readonly name: string,
19
+ public readonly attributes: readonly AttioAttributeDefinition[] = []
20
+ ) {}
21
+
22
+ declare readonly __recordValues: TRecordValues
23
+ declare readonly __inputValues: TInputValues
24
+ {{#if objects.length}}
25
+
26
+ {{#each objects}}
27
+ static {{staticName}} = new AttioObject<"{{escape apiSlug}}", {{{recordValuesType}}}, {{{inputValuesType}}}>("{{escape apiSlug}}", "{{escape singularNoun}}", {{{attributeSource}}})
28
+ {{/each}}
29
+ {{/if}}
30
+ }
31
+
32
+ export const Attio = {
33
+ /** Use in `skills[]` */
34
+ skill(opts: { object: AttioObject<any> }): TypedSkill<{{{skillToolType}}}> {
35
+ return new AttioOutputConfig("{{escape id}}", opts.object.apiSlug) as TypedSkill<{{{skillToolType}}}>
36
+ },
37
+ }
38
+
@@ -0,0 +1,11 @@
1
+ {{sectionHeader "Datadog"}}
2
+
3
+ {{> resource-class indexClass}}
4
+
5
+ export const Datadog = {
6
+ /** Use in `skills[]` */
7
+ skill(opts?: { indexes?: DatadogIndex[] }): TypedSkill<{{{skillToolType}}}> {
8
+ return new DatadogConfig("{{escape id}}", opts?.indexes?.map(i => i.name)) as TypedSkill<{{{skillToolType}}}>
9
+ },
10
+ }
11
+
@@ -0,0 +1,13 @@
1
+ {{sectionHeader "Figma"}}
2
+
3
+ export const Figma = {
4
+ /** Trigger when a comment is added to a Figma file */
5
+ onComment(opts: { fileKey: string; fileName: string; teamId: string }): FigmaConfig {
6
+ return new FigmaConfig("{{escape id}}", opts.fileKey, opts.fileName, opts.teamId, [FigmaEventType.FILE_COMMENT])
7
+ },
8
+ /** Trigger on all Figma events */
9
+ trigger(opts: { fileKey: string; fileName: string; teamId: string; eventTypes?: FigmaEventType[] }): FigmaConfig {
10
+ return new FigmaConfig("{{escape id}}", opts.fileKey, opts.fileName, opts.teamId, opts.eventTypes)
11
+ },
12
+ }
13
+
@@ -0,0 +1,62 @@
1
+ {{sectionHeader "GitHub"}}
2
+
3
+ export class GithubOwner {
4
+ constructor(public readonly name: string) {}
5
+ {{#if owners.length}}
6
+
7
+ {{#each owners}}
8
+ static {{staticName}} = new GithubOwner("{{escape name}}")
9
+ {{/each}}
10
+ {{/if}}
11
+ }
12
+
13
+ export class Repos {
14
+ constructor(
15
+ public readonly repositoryId: number,
16
+ public readonly name: string,
17
+ public readonly owner: GithubOwner,
18
+ public readonly fullName: string
19
+ ) {}
20
+ {{#if repoGroups.length}}
21
+
22
+ {{#each repoGroups}}
23
+ static {{ownerStaticName}} = {
24
+ {{#each repos}}
25
+ {{staticName}}: new Repos({{id}}, "{{escape name}}", GithubOwner.{{../ownerStaticName}}, "{{escape fullName}}"),
26
+ {{/each}}
27
+ } as const
28
+ {{/each}}
29
+ {{/if}}
30
+ }
31
+
32
+ export const GitHub = {
33
+ /** Trigger on push to a repository */
34
+ onPush(opts: { repo: Repos }): TypedTrigger<GithubPushInputEvent> {
35
+ return new GitHubConfig("{{escape id}}", [opts.repo.repositoryId], [GitHubEventType.PUSH]) as TypedTrigger<GithubPushInputEvent>
36
+ },
37
+ /** Trigger when a pull request is opened */
38
+ onPROpened(opts: { repo: Repos }): TypedTrigger<GithubPRInputEvent> {
39
+ return new GitHubConfig("{{escape id}}", [opts.repo.repositoryId], [GitHubEventType.PR_OPENED]) as TypedTrigger<GithubPRInputEvent>
40
+ },
41
+ /** Trigger when a pull request is merged */
42
+ onPRMerged(opts: { repo: Repos }): TypedTrigger<GithubPRInputEvent> {
43
+ return new GitHubConfig("{{escape id}}", [opts.repo.repositoryId], [GitHubEventType.PR_MERGED]) as TypedTrigger<GithubPRInputEvent>
44
+ },
45
+ /** Trigger when a pull request is closed */
46
+ onPRClosed(opts: { repo: Repos }): TypedTrigger<GithubPRInputEvent> {
47
+ return new GitHubConfig("{{escape id}}", [opts.repo.repositoryId], [GitHubEventType.PR_CLOSED]) as TypedTrigger<GithubPRInputEvent>
48
+ },
49
+ /** Trigger on any pull request event */
50
+ onPR(opts: { repo: Repos }): TypedTrigger<GithubPRInputEvent> {
51
+ return new GitHubConfig("{{escape id}}", [opts.repo.repositoryId], [GitHubEventType.PR_OPENED, GitHubEventType.PR_MERGED, GitHubEventType.PR_CLOSED, GitHubEventType.PR_SYNCHRONIZE]) as TypedTrigger<GithubPRInputEvent>
52
+ },
53
+ /** Trigger on specific GitHub events for the given repositories */
54
+ trigger(opts: { repos: Repos[]; eventTypes?: GitHubEventType[] }): TypedTrigger<GithubInputEvent> {
55
+ return new GitHubConfig("{{escape id}}", opts.repos.map(r => r.repositoryId), opts.eventTypes) as TypedTrigger<GithubInputEvent>
56
+ },
57
+ /** Use in `skills[]` */
58
+ skill(opts: { repos: Repos[] }): TypedSkill<{{{skillToolType}}}> {
59
+ return new GitHubConfig("{{escape id}}", opts.repos.map(r => r.repositoryId)) as TypedSkill<{{{skillToolType}}}>
60
+ },
61
+ }
62
+
@@ -0,0 +1,21 @@
1
+ {{sectionHeader "Gmail"}}
2
+
3
+ export const Gmail = {
4
+ /** Trigger when a new email is received */
5
+ onEmail(): GmailConfig {
6
+ return new GmailConfig("{{escape id}}", [GmailEventType.EMAIL_RECEIVED])
7
+ },
8
+ /** Trigger on all Gmail events */
9
+ trigger(opts?: { eventTypes?: GmailEventType[] }): GmailConfig {
10
+ return new GmailConfig("{{escape id}}", opts?.eventTypes)
11
+ },
12
+ /** Use in `skills[]` */
13
+ skill(): TypedSkill<{{{skillToolType}}}> {
14
+ return new GmailOutputConfig("{{escape id}}") as TypedSkill<{{{skillToolType}}}>
15
+ },
16
+ /** Use in `skills[]` — creates draft emails */
17
+ draftSkill(): TypedSkill<{{{skillToolType}}}> {
18
+ return new GmailDraftOutputConfig("{{escape id}}") as TypedSkill<{{{skillToolType}}}>
19
+ },
20
+ }
21
+
@@ -0,0 +1,12 @@
1
+ // Auto-generated by `terse generate` — do not edit manually
2
+ // Re-run `terse generate` to update.
3
+
4
+ {{#if useMultilineImports}}
5
+ import {
6
+ {{#each imports}}
7
+ {{this}},
8
+ {{/each}}
9
+ } from "terse-sdk"
10
+ {{else}}
11
+ import { {{#each imports}}{{this}}{{#unless @last}}, {{/unless}}{{/each}} } from "terse-sdk"
12
+ {{/if}}
@@ -0,0 +1,11 @@
1
+ {{sectionHeader "LaunchDarkly"}}
2
+
3
+ {{> resource-class projectClass}}
4
+
5
+ export const LaunchDarkly = {
6
+ /** Use in `skills[]` */
7
+ skill(opts: { project: LaunchDarklyProject; environmentKeys: string[] }): TypedSkill<{{{skillToolType}}}> {
8
+ return new LaunchDarklyConfig("{{escape id}}", opts.project.projectKey, opts.environmentKeys) as TypedSkill<{{{skillToolType}}}>
9
+ },
10
+ }
11
+
@@ -0,0 +1,27 @@
1
+ {{sectionHeader "Linear"}}
2
+
3
+ {{> resource-class teamClass}}
4
+
5
+ export const Linear = {
6
+ /** Trigger when a new issue is created */
7
+ onIssueCreated(opts?: { projectId?: string; projectName?: string }): LinearInputConfig {
8
+ return new LinearInputConfig("{{escape id}}", opts?.projectId, opts?.projectName, [LinearEventType.ISSUE_CREATED])
9
+ },
10
+ /** Trigger when an issue is updated */
11
+ onIssueUpdated(opts?: { projectId?: string; projectName?: string }): LinearInputConfig {
12
+ return new LinearInputConfig("{{escape id}}", opts?.projectId, opts?.projectName, [LinearEventType.ISSUE_UPDATED])
13
+ },
14
+ /** Trigger when a comment is added to an issue */
15
+ onComment(opts?: { projectId?: string; projectName?: string }): LinearInputConfig {
16
+ return new LinearInputConfig("{{escape id}}", opts?.projectId, opts?.projectName, [LinearEventType.COMMENT_CREATED])
17
+ },
18
+ /** Trigger on all Linear events */
19
+ trigger(opts?: { projectId?: string; projectName?: string; eventTypes?: LinearEventType[] }): LinearInputConfig {
20
+ return new LinearInputConfig("{{escape id}}", opts?.projectId, opts?.projectName, opts?.eventTypes)
21
+ },
22
+ /** Use in `skills[]` */
23
+ skill(opts?: { team?: LinearTeam; projectId?: string; projectName?: string }): TypedSkill<{{{skillToolType}}}> {
24
+ return new LinearOutputConfig("{{escape id}}", opts?.team?.teamId, opts?.team?.name, opts?.projectId, opts?.projectName) as TypedSkill<{{{skillToolType}}}>
25
+ },
26
+ }
27
+
@@ -0,0 +1,15 @@
1
+ {{sectionHeader "Notion"}}
2
+
3
+ {{> resource-class databaseClass}}
4
+
5
+ {{> resource-class pageClass}}
6
+
7
+ export const Notion = {
8
+ /** Use in `skills[]` */
9
+ skill(opts?: { databases?: NotionDatabase[]; pages?: NotionPage[] }): TypedSkill<{{{skillToolType}}}> {
10
+ return new NotionConfig("{{escape id}}",
11
+ opts?.databases?.map(d => d.databaseId), opts?.databases?.map(d => d.title),
12
+ opts?.pages?.map(p => p.pageId), opts?.pages?.map(p => p.title)) as TypedSkill<{{{skillToolType}}}>
13
+ },
14
+ }
15
+
@@ -0,0 +1,11 @@
1
+ {{sectionHeader "PostHog"}}
2
+
3
+ {{> resource-class projectClass}}
4
+
5
+ export const Posthog = {
6
+ /** Use in `skills[]` */
7
+ skill(opts: { project: PosthogProject }): TypedSkill<{{{skillToolType}}}> {
8
+ return new PosthogConfig("{{escape id}}", opts.project.projectId, opts.project.name) as TypedSkill<{{{skillToolType}}}>
9
+ },
10
+ }
11
+
@@ -0,0 +1,9 @@
1
+ export class {{className}} {
2
+ constructor({{{constructorParams}}}) {}
3
+ {{#if items.length}}
4
+
5
+ {{#each items}}
6
+ static {{staticName}} = new {{../className}}({{{argsText}}})
7
+ {{/each}}
8
+ {{/if}}
9
+ }